For version 0.7.0.
To top.

Interactive mode


When learning Shapes, it may be useful to try out the language with the compiler operating in interactive mode. This means that you give small chunks of code to the compiler, and the compiler replies with the value of that chunk. This part of the documentation discusses how this mode can be useful, and explains a few differences in language semantics for this mode.
Sections:    Launching the interactive mode    Semantics    Typing input in the interactive mode    Continuations available in the interactive mode    Debugging using the interactive mode    Support for interactive mode in Emacs

Launching the interactive mode

How to start the compiler in interactive mode is explained in the man page. Here, we just give an example; try running
shapes -i --xpdf
and start typing! You may exit by terminating the input stream.
To give an idea of what working with the interactive mode is like, a short sample session is included below:
TiddeBook:source tidde$ ./shapes -i --xpdf --split=flat
[ #0] 1 + 2
 #0 ⇒  3
[ #1] f: \ n → [if ( n = '0 ) '1 ( n * [f n-'1] )]
[ #2] a: '5
[ #3] g: \ m → [f b + m]
#3:1(12-13): The variable b is unbound.
[ #4] [f 5]
 { interactive }	interaction
 #1:1(19-21)	binary infix's second
#1:1(17-18): Operator = not defined for (Float,Integer) at #1:1(15-16), #1:1(19-21).
[debug][ #5] :ce
[ #6] [f a]
 #6 ⇒  '120
[ #7] pic: [stroke (0cm,0cm)>(2%C^100°)--(1cm,1cm)]
[ #8] pic
#File: /Users/tidde/shapes/source/#shapes-8.pdf
[ #9] •page << @width:1cm | @spot
[#10] (•page)
#File: /Users/tidde/shapes/source/#shapes-10.pdf
[#11] •page << pic
[#12] (•page)
#File: /Users/tidde/shapes/source/#shapes-12.pdf
[#13] <<EOF>>
Note the error recovery which has no counter-part in non-interactive mode. Also note that §Drawable results are output to files that are displayed automatically by a pdf previewer. The non-standard input :ce is used to abandon the debugging context that was set up to handle type mismatch error. This and many other features of the interactive mode will be described in the following sections.

Semantics

The difference between interactive and non-interactive evaluation is that the list of bindings in a code bracket (environment) need not be completely known when evaluating expressions in that bracket (environment). The ability to modify the set of bindings is restricted to certain imaginary code brackets, ensuring that the bindings in existing code are never affected.
While bindings cannot be affected, there are some things that can be done in the interactive mode:
If an expression results in a §Drawable “being returned to” the imaginary code bracket, the result is written to a file, and the name of the file is given on stdout. The details of where the file is placed, and its name, are explained in the man page. Let it just be mentioned here, that there is a default, so one does not have to specify anything on the command line. If a previewer is opted for, it will be invoked to display the file.

Typing input in the interactive mode

The interactive mode is meant to be used more or less interactively. For the typing of input, this means that the source shall be given in chunks that can be evaluated individually, rather than as a continuous stream of characters. Chunks consisting of just one line can be typed as is, but to enter input with more than one line one must prevent that the newline at the end of each line is interpreted as the end of the chunk. In Shapes non-end-of-chunk newlines must be preceded with the space character (many other language use the backslash instead of the space character for this purpose, but this is not a good option for Shapes since the backslash is a token by itself).
Having to remember the space before each in-chunk newline can be very annoying, but it is even worse that once a line has been entered, there is no way to go back and change it. The rationale is that users are not expected to type chunks that consist of more than one line directly at the Shapes compiler prompt, but the space-newline sequence shall just be considered part of a simple protocol that can be used by external command editors (such as Emacs with shapes-mode).
Since the interactive mode is not only used for play, but also for debugging, and many of the actions a user takes while debugging are commanded through •db, the interactive mode gives fast access to the methods of this state. The easy convention that makes this possible is that if an input begins with a colon, the rest of the input is placed within the brackets of a mutator call to •db. For instance,
:bi circle
is expanded to
#db.[bi circle]
(where §•Debugger/bi is an alias for §•Debugger/breakIn). Note that this convention does not interfere with normal input, since each normal chunk of input must be complete in itself, and hence cannot begin with a colon.

Continuations available in the interactive mode

There are two special continuations that are provided in the dynamic environment of the interactive evaluation.
One continuation is named ..Shapes..Debug..C—bye, and may be used to exit the interactive mode without terminating the input stream. Unlike when exiting by terminating the input stream, no message is printed before the Shapes compiler exits. The value passed to this continuation determined the exit code from the Shapes compiler. An integer outside the reserved range [ -99, 99 ] is returned as is, and the symbols { 'OK 'GenericError 'UserDetectedError } may be used to refer to some of the exit codes (in the reserved range) specified in the man page.
The other continuation is named ..Shapes..Debug..C—top_repl (read as top level read-eval-print-loop), and gives access to the continuation used to drive the interactive mode. It is not clear that this can be useful, as long as continuations are limited to exist only in the dynamic environment. For instance, a debugging tool like the following function is not possible to define as the language is defined today:
break: \ val → (escape_continuation c (escape_continue top_repl (> cont:c result:val <)))
The following short session illustrates the effects of invoking the two continuations discussed in this section.
TiddeBook:source tidde$ ./shapes -i
[ #1] break: \ val → (escape_continue top_repl val)
[ #2] f: \ a b → a + [break b]
[ #3] •stdout << [f 1+2 3+4] << `Howdy´
 #3 ⇒  7
[ #4] quit: \ → (escape_continue bye 'OK)
[ #5] •stdout << `Time to quit!´ << "{n} << [quit] << `Goodbye´
Time to quit!
TiddeBook:source tidde$ echo $?
0
TiddeBook:source tidde$ 

Debugging using the interactive mode

In every environment that is set up for interactive evaluation there is a •db state, and it is through this the user can do typical debugging tasks such as setting breakpoints, stepping the program, and inspecting various aspects of the evaluation. The state •db is of type §•Debugger, and the documentation of this type is to be considered the main reference for •db. In this section, •db is described in tutorial style, along with some additional information that is key to understanding the debugging facility.
Most mutators of §•Debugger have abbreviated aliases to allow them to be typed quickly at the debug prompt. Here, the abbreviated forms will be used in code examples, while the longer names are used in running text. If there is an abbreviated form, it can always be found by following the link to the reference documentation for the mutator in question.

Getting started

A breakpoint is set at an expression. Instead of evaluating the expression, a new code bracket is set up at the point of the expression, the expression is stored as a thunk bound to expr, the continuation for the expression is bound to ..Shapes..Debug..C—resume, a •db is set up with relevant parameters, and interactive mode is entered in that environment. Now, the user may examine expressions, modify states, introduce temporary bindings, and so forth. When happy and ready to continue, the normal action to take is to type
:r
but if program evaluation was interrupted because of an error, this will probably just trigger the same error again. To recover from an error one must resume evaluation with an expression that replaces the one that caused the error, or one may just abort the debugging session by escaping to the top level interaction by means of ..Shapes..Debug..C—top_repl.

The debug prompt

The prompt that asks for input will indicate that evaluation takes place in a debugging context. If another error occurs while debugging, this may or may not set up a new debugging context — some logic is used to try to decide when setting up a new context would just be annoying. The following sample session shows how the prompt changes, and reuse of the debugging context.
[ #0] f: \ a b → 10 * a + b
[ #1] [f 1]
 { interactive }        interaction
{ interactive }: Among the formals at #0:1(5-8), the following variables are missing: (1)b.
Reusing interactive continuation.
[ #2] [f '2 3]
 #2 ⇒  23
[ #3] [f 2 '3]
 { interactive }        interaction
 #0:1(20-21)    binary infix's second
#0:1(18-19): Operator + not defined for (Float,Integer) at #0:1(11-17), #0:1(20-21).
[debug][ #4] 10 + '5
 #3:1(5-7)      interaction with resume
 #4:1(5-7)      binary infix's second
#4:1(3-4): Operator + not defined for (Float,Integer) at #4:1(0-2), #4:1(5-7).
[debug,debug][ #5] :ba `badfun.shape´ 1 2
 #4:1(5-7)      interaction with resume
#5:1(0-27): Core function breakAt, argument at #5:1(23-24): expected a Integer, got a Float.
Reusing interactive continuation.
[debug,debug][ #6] :r 5
 #4 ⇒  15
[debug][ #7] :r 4
 #3 ⇒  24
[ #8] 

Examining the context

Besides examining the lexical and dynamic environments by simply evaluating individual lexical or dynamic variables, •db can be used to get information about all available bindings (but the core bindings are omitted for brevity) by means of §•Debugger/env and §•Debugger/dyn. Similarly, a the backtrace returned by §•Debugger/backtrace displays important information about the chain of continuations and possibly nested debugging contexts. There is not much to say about these mutators (yes, they are mutators since they are accessed via a state), so we turn to more interesting features of the debugger.

Breakpoints

Each expression may have a breakpoint, and these are the ways of referring to a breakpoint or the corresponding expression: Note that not every expression may be possible to refer to by any of these methods. For instance, two expressions may sometimes have exactly the same source locations.
Once an expression is given a breakpoint, one may set an ignore count (using §•Debugger/breakIgnore) to inactivate the breakpoint for a given number of visits. This can be useful, for instance, if the expression appears in a loop, and one knows that it is only after a number of iterations that the expression is interesting to stop at.
To clear breakpoints, one has §•Debugger/breakClear that clears just one expression (defaulting to the current expression), and §•Debugger/breakClearAll that clears all breakpoints.

Stepping

Stepping the evaluation of a lazy language is a rather strange thing to do since the way evaluation jumps from one expression to the next is often hard to predict. On the other hand, stepping the evaluation may be a good way for someone who is not used to lazy evaluation to learn how it works.
Currently, the interactive mode (for it is the mode of evaluation rather than a feature of •db that makes stepping possible) supports two modes of stepping. The simplest way to step evaluation is to stop at every expression, §•Debugger/stepe is the way a user may access this mode.
Sometimes, stopping at every expression becomes tedious and one would like to make bigger steps. One way to do so is to stop at the first expression which is not lexigraphically inside the current expression, and the corresponding mutator of •db is §•Debugger/step. Just as with §•Debugger/stepe, it may be very hard to predict the destination of §•Debugger/step.
One way to step in a more predictable manner is obviously to temporarily set breakpoints at the desired destinations, and this idea is supported internally by the interactive mode. There are currently two mutators that use this mechanism; §•Debugger/stepc sets temporary breakpoints in all child expressions of the current expression, while §•Debugger/steps sets temporary breakpoints in all sibling expressions.

Going up the backtrace

While the mutators §•Debugger/resume and §•Debugger/top_repl gives convenient access to certain continuations, and other continuations in the dynamic environment may be invoked manually, there are still many anonymous continuations that can be seen in the backtrace, but which are not accessible by any of the tools described so far. To serve this need, §•Debugger/contExit allows an interactive context to be exited by simply picking an interactive continuation from the backtrace. To reach a continuation between the bottom of the backtrace and the next interactive backtrace, one has to use §•Debugger/contUp.
Note that a common use of §•Debugger/contExit is to abandon an interactive context that was entered because of some unfortunate typo at the debug prompt, as the next example shows.
[ #0] 1+'2
 { interactive }	interaction
 #0:1(2-4)	binary infix's second
#0:1(1-2): Operator + not defined for (Float,Integer) at #0:1(0-1), #0:1(2-4).
[debug][ #1] :rr 2
 #0:1(2-4)	interaction with resume
 #1:1(5-7)	function call's function
#1:1(5-7): State type MetaClass has no mutator called rr.
[debug,debug][ #2] :ce
[debug][ #3] :r 2
 #0 ⇒  3
[ #4] 

Support for interactive mode in Emacs

Once you have installed shapes-mode in Emacs, type M-x run-shapes to start the interactive mode inside an Emacs buffer. Try producing a §Drawable value — it's fun!
Screenshot of the shapes-mode interface to the interactive mode of the Shapes compiler.
Get Shapes at SourceForge.net. Fast, secure and Free Open Source software downloads