1 2\subsection{Loading and Using} 3The package is loaded using \texttt{load\_package} or 4\texttt{load!-package} in algebraic or symbolic mode, resp. There is a 5central switch \sw{assert}, which is off by default. With 6\sw{assert} off, all type definitions and assertions described 7in the sequel are ignored and have the status of comments. For 8verification of the assertions it most be turned on (dynamically) before 9the first relevant type definition or assertion. 10 11ASSERT aims at the dynamic analysis of RLISP expr procedure in symbolic 12mode. All uses of \texttt{typedef} and \texttt{assert} discussed in the 13following have to take place in symbolic mode. There is, in contrast, a 14final print routine \texttt{assert\_analyze} that is available in both 15symbolic and algebraic mode. 16 17\subsection{Type Definitions} 18Here are some examples for definitions of types: 19\begin{verbatim} 20typedef any; 21typedef number checked by numberp; 22typedef sf checked by sfpx; 23typedef sq checked by sqp; 24\end{verbatim} 25The first one defines a type \texttt{any}, which is not possibly checked 26by any function. This is useful, e.g., for functions which admit any 27argument at one position but at others rely on certain types or 28guarantee certain result types, e.g., 29\begin{verbatim} 30procedure cellcnt(a); 31 % a is any, returns a number. 32 if not pairp a then 0 else cellcnt car a + cellcnt cdr a + 1; 33\end{verbatim} 34 35The other ones define a type \texttt{number}, which can be checked by 36the RLISP function \texttt{numberp}, a type \texttt{sf} for standard 37forms, which can be checked by the function \texttt{sfpx} provided by 38ASSERT, and similarly a type for standard quotients. 39 40All type checking functions take one argument and return extended 41Boolean, i.e., non-nil iff their argument is of the corresponding type. 42 43\subsection{Assertions} 44Having defined types, we can formulate assertions on expr procedures in 45terms of these types: 46\begin{verbatim} 47assert cellcnt: (any) -> number; 48assert addsq: (sq,sq) -> sq; 49\end{verbatim} 50Note that on the argument side parenthesis are mandatory also with only 51one argument. This notation is inspired by Haskell but avoids the 52intuition of currying.\footnote{This notation has benn suggested by C. Zengler} 53 54Assertions can be dynamically checked only for expr procedures. When 55making assertions for other types of procedures, a warning is issued and 56the assertion has the status of a comment. 57 58It is important that assertions via assert come after the definitions of 59the used types via \texttt{typedef} and also after the definition of the 60procedures they make assertions on. 61 62A natural order for adding type definitions and assertions to the source 63code files would be to have all typedefs at the beginning of a module 64and assertions immediately after the respective functions. 65Fig.~\ref{FIG:assMod} illustrates this. Note that for dynamic checking 66of the assertions the switch \sw{assert} has to be on during 67the translation of the module; i.e., either when reading it with 68\texttt{in} or during compilation. For compilation this can be achieved 69by commenting in the \texttt{on assert} at the beginning or by 70parameterizing the Lisp-specific compilation scripts in a suitable way. 71 72An alternative option is to have type definitions and assertions for 73specific packages right after \texttt{load\_package} in batch files as 74illustrated in Fig.~\ref{FIG:assBat}. 75\begin{figure} 76 \begin{small} 77\begin{verbatim} 78module sizetools; 79 80load!-package 'assert; 81 82% on assert; 83 84typedef any; 85typedef number checked by number; 86 87procedure cellcnt(a); 88 % a is any, returns a number. 89 if not pairp a then 0 else cellcnt car a + cellcnt cdr a + 1; 90 91assert cellcnt: (any) -> number; 92 93% ... 94 95endmodule; 96 97end; % of file 98\end{verbatim} 99 \end{small} 100 \caption{Assertions in the source code.}\label{FIG:assMod} 101\end{figure} 102 103\begin{figure} 104 \begin{small} 105\begin{verbatim} 106load_package sizetools; 107load_package assert; 108 109on assert; 110 111lisp << 112 typedef any; 113 typedef number checked by numberp; 114 115 assert cellcnt: (any) -> number 116>>; 117 118% ... computations ... 119 120assert_analyze(); 121 122end; % of file 123\end{verbatim} 124 \end{small} 125 \caption{Assertions in a batch file.}\label{FIG:assBat} 126\end{figure} 127 128\subsection{Dynamic Checking of Assertions} 129Recall that with the switch \sw{assert} off at translation 130time, all type definitions and assertions have the status of comments. 131We are now going to discuss how these statements are processed with 132\sw{assert} on. 133 134\texttt{typedef} marks the type identifier as a valid type and possibly 135associates the given typechecking function with it. Technically, the 136property list of the type identifier is used for both purposes. 137 138\texttt{assert} encapsulates the procedure that it asserts on into 139another one, which checks the types of the arguments and of the result 140to the extent that there are typechecking functions given. Whenever some 141argument does not pass the test by the typechecking function, there is a 142warning message issued. Furthermore, the following numbers are counted 143for each asserted function: 144\begin{enumerate} 145\item The number of overall calls, 146\item the number of calls with at least one assertion violation, 147\item the number of assertion violations. 148\end{enumerate} 149These numbers can be printed anytime in either symbolic or algebraic 150mode using the command \texttt{assert\_analyze()}. This command at the 151same time resets all the counters. 152 153Fig.~\ref{FIG:sample} shows an interactive sample session. 154% 155\begin{figure} 156 \begin{small} 157\begin{verbatim} 1581: symbolic$ 159 1602* load_package assert$ 161 1623* on assert$ 163 1644* typedef sq checked by sqp; 165sqp 166 1675* assert negsq: (sq) -> sq; 168+++ negsq compiled, 13 + 20 bytes 169 170(negsq) 171 1726* assert addsq: (sq,sq) -> sq; 173+++ addsq compiled, 14 + 20 bytes 174 175(addsq) 176 1777* addsq(simp 'x,negsq simp 'y); 178 179((((x . 1) . 1) ((y . 1) . -1)) . 1) 180 1818* addsq(simp 'x,negsq numr simp 'y); 182 183*** assertion negsq: (sq) -> sq violated by arg1 (((y . 1) . 1)) 184 185*** assertion negsq: (sq) -> sq violated by result (((y . -1) . -1)) 186 187*** assertion addsq: (sq,sq) -> sq violated by arg2 (((y . -1) . -1)) 188 189*** assertion addsq: (sq,sq) -> sq violated by result (((y . -1) . -1)) 190 191(((y . -1) . -1)) 192 1939* assert_analyze()$ 194------------------------------------------------------------------------ 195function #calls #bad calls #assertion violations 196------------------------------------------------------------------------ 197addsq 2 1 2 198negsq 2 1 2 199------------------------------------------------------------------------ 200sum 4 2 4 201------------------------------------------------------------------------ 202\end{verbatim} 203 \end{small} 204 \caption{An interactive sample session.}\label{FIG:sample} 205\end{figure} 206 207\subsection{Switches} 208\ttindextype{switch}{ASSERT} 209\ttindextype{switch}{ASSERTBREAK} 210\ttindextype{switch}{ASSERTSTATISTICS} 211\hypertarget{switch:ASSERT}{} 212\hypertarget{switch:ASSERTBREAK}{} 213\hypertarget{switch:ASSERTSTATISTICS}{} 214 215As discussed above, the switch \sw{assert} controls at 216translation time whether or not assertions are dynamically checked. 217 218There is a switch \sw{assertbreak}, which is off by default. When 219on, there are not only warnings issued for assertion violations but the 220computations is interrupted with a corresponding error. 221 222The statistical counting of procedure calls and assertion violations is 223toggled by the switch \sw{assertstatistics}, which is on by default. 224 225\subsection{Efficiency} 226The encapsulating functions introduced with assertions are automatically 227compiled. 228 229% sturm@lennier[~/Desktop] time redpsl < taylor.tst > /dev/null 230 231% real 0m0.798s 232% user 0m0.584s 233% sys 0m0.166s 234% sturm@lennier[~/Desktop] time redcsl < taylor.tst > /dev/null 235 236% real 0m1.975s 237% user 0m1.808s 238% sys 0m0.143s 239% sturm@lennier[~/Desktop] time redpsl < taylor.tst > /dev/null 240 241% real 0m0.442s 242% user 0m0.316s 243% sys 0m0.122s 244% sturm@lennier[~/Desktop] time redcsl < taylor.tst > /dev/null 245 246% real 0m0.610s 247% user 0m0.491s 248% sys 0m0.115s 249 250We have experimentally checked assertions on the standard quotient 251arithmetic \texttt{addsq}, \texttt{multsq}, \texttt{quotsq}, 252\texttt{invsq}, \texttt{negsq} for the test file \texttt{taylor.tst} of 253the TAYLOR package. For CSL we observe a slowdown of factor 3.2, and for 254PSL we observe a slowdown of factor 1.8 in this particular example, 255where there are 323\,750 function calls checked altogether. 256 257The ASSERT package is considered an analysis and debugging tool. 258Production system should as a rule not run with dynamic assertion 259checking. For critical applications, however, the slowdown might be 260even acceptable. 261 262\subsection{Possible Extensions} 263Our assertions could be used also for a static type analysis of source 264code. In that case, the type checking functions become irrelevant. On 265the other hand, the introduction of variouse unchecked types becomes 266meaningful. 267 268In a model, where the source code is systematically annotated with 269assertions, it is technically no problem to generalize the specification 270of procedure definitions such that assertions become implicit. For 271instance, one could \emph{optionally} admit procedure definitions like 272the following: 273\begin{verbatim} 274procedure cellcnt(a:any):number; 275 if not pairp a then 0 else cellcnt car a + cellcnt cdr a + 1; 276\end{verbatim} 277 278