For Programmers: Free Programming Magazines  


Home > Archive > Scheme > July 2004 > Continuations and REPL









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Continuations and REPL
Marcin 'Qrczak' Kowalczyk

2004-07-11, 4:00 pm

How should a REPL work with multiple expressions / definitions, errors,
and continuations? First, multiple expressions, and cases where I think
I know the answer. Probably in some cases there is no right answer, so
I'm asking what do you think is more useful, consistent etc.:

1. The user enters "(+ 2 2) (+3 3)". I assume that both results should be
printed. Right?

2. The user enters "(begin 1 2 3)". This is a toplevel 'begin', but
I assume that only 3 should be printed, as with expression 'begin'.

3. The user enters "(begin 1 (define x 2) 3)". This shows that this is
really a toplevel 'begin', x is defined, but still only 3 is printed.

4. The user enters "(+ 2". The REPL shows a modified prompt for incomplete
input and waits for more. The user enters "3) 6" in the next line. The
interpreter responds with "5" and "6".

5. The user enters "4 (+ 2". The repl waits for more. The user enters
"3)". The interpreter responds with "4" and "5". This is not as
obvious as the previous case, but easier to do and consistent.
Or should "4" be printed before the rest is entered?

6. The user enters "1 #z 2". This is a syntax error which is reported;
no expression is evaluated nor has its result printed. Right?

7. The user enters "4 (+ 2". The REPL waits for more. The user enters
"#z". The interpreter reports the syntax error and forgets the pending
input, going back to regular prompt. Right? Or should it expect only
the last line to be reentered?

8. The user enters "4 (+ 2". The REPL waits for more. The user presses
Enter without entering anything. Is it a good idea to let this mean
"I don't indend to enter anything more, perhaps I mixed up parentheses"
so the interpreter can report a syntax error about incomplete input?
This prevents entering empty lines in the middle of input, which might
be bad for copy & paste. But otherwise there is no way for the user to
tell "forget about what I entered so far" except by trying to provoke
a syntax error. Intercepting ^C is hard for technical reasons.

9. The user enters "(/ 1 0) 2". The runtime error resulting from the first
expression is reported, and the second expression is executed. Or
should it not be executed?

10. The user enters "(begin (define k (call/cc (lambda (k) k))) 1)", then
"(k 2)". k gets redefined to 2, but is 1 evaluated and printed again?

11. The user enters "(define k (call/cc (lambda (k) k))) 1", then "(k 2)".
Same question as above.

12. The user enters "(define k (call/cc (lambda (k) k)))", then "1", then
"(k 2)". Same question as above.

13. Expressions are read from a file instead of being entered
interactively - either because the interpreter was invoked differently,
or because the user used 'load'. I assume that results of individual
expressions should not be printed (R5RS gives freedom, but this is
more useful I think). Should definitions be executed while they are
parsed, or after all are parsed? Should a parse error abort processing
of the whole file? Should a runtime error abort execution of the whole
file? Should a continuation include evaluation of the rest of the file?
Let's assume we don't have a debugger nor program-level catching of
errors, so the only thing possible is to abort to some point. Or maybe
some non-R5RS means of catching errors are well-established?

--
__("< Marcin Kowalczyk
\__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

Taylor Campbell

2004-07-18, 8:59 pm

Marcin 'Qrczak' Kowalczyk <qrczak@knm.org.pl> wrote in message news:<pan.2004.07.11.19.48.08.500011@knm.org.pl>...
> How should a REPL work with multiple expressions / definitions, errors,
> and continuations? First, multiple expressions, and cases where I think
> I know the answer. Probably in some cases there is no right answer, so
> I'm asking what do you think is more useful, consistent etc.:


I'll respond with my own opinions, and explanation where due.

> 1. The user enters "(+ 2 2) (+3 3)". I assume that both results should be
> printed. Right?


Yes. (I assume you mean (+ 3 3), not (+3 3).)

> 2. The user enters "(begin 1 2 3)". This is a toplevel 'begin', but
> I assume that only 3 should be printed, as with expression 'begin'.


Yes.

> 3. The user enters "(begin 1 (define x 2) 3)". This shows that this is
> really a toplevel 'begin', x is defined, but still only 3 is printed.


Yes.

> 4. The user enters "(+ 2". The REPL shows a modified prompt for incomplete
> input and waits for more. The user enters "3) 6" in the next line. The
> interpreter responds with "5" and "6".


Yes, although I'd personally prefer no prompt.

> 5. The user enters "4 (+ 2". The repl waits for more. The user enters
> "3)". The interpreter responds with "4" and "5". This is not as
> obvious as the previous case, but easier to do and consistent.
> Or should "4" be printed before the rest is entered?


I think 4 should be printed, and then the REPL should wait. Each
expression should be evaluated at a time, and, until reached, left in
the input stream. This allows, for example, for one to send

(read) foo

to the REPL and have it work as expected. Use BEGIN is you really want
guaranteed sequential expression evaluation (assuming the REPL's
environment is regular Scheme).

> 6. The user enters "1 #z 2". This is a syntax error which is reported;
> no expression is evaluated nor has its result printed. Right?


No. As above, it should get the result of 1 and then continue.

> 7. The user enters "4 (+ 2". The REPL waits for more. The user enters
> "#z". The interpreter reports the syntax error and forgets the pending
> input, going back to regular prompt. Right? Or should it expect only
> the last line to be reentered?


It should terminate at the erroneous expression and ditch the rest, to
be consistent with reading & evaluating each expression at a time.

> 8. The user enters "4 (+ 2". The REPL waits for more. The user presses
> Enter without entering anything. Is it a good idea to let this mean
> "I don't indend to enter anything more, perhaps I mixed up parentheses"
> so the interpreter can report a syntax error about incomplete input?
> This prevents entering empty lines in the middle of input, which might
> be bad for copy & paste. But otherwise there is no way for the user to
> tell "forget about what I entered so far" except by trying to provoke
> a syntax error. Intercepting ^C is hard for technical reasons.


What's wrong with ^C?

> 9. The user enters "(/ 1 0) 2". The runtime error resulting from the first
> expression is reported, and the second expression is executed. Or
> should it not be executed?


The user should be thrown into a debugger or something, and whatever it
is thrown into should receive the 2; this is consistent with evaluating
& reading the expressions one at a time, and, though it's a little
annoying if the user is thrown directly into a debugger, it would be
less annoying if merely a recursive REPL level were pushed -- running
like a regular REPL -- and the user were given an opportunity to launch
the debugger himself. (See Scheme48's REPL/command processor for an
example of what I mean here with a recursive REPL.)

> 10. The user enters "(begin (define k (call/cc (lambda (k) k))) 1)", then
> "(k 2)". k gets redefined to 2, but is 1 evaluated and printed again?


Yes. It's all one expression to be evaluated, and as such should be
consistent with regular Scheme expression evaluation rules.

> 11. The user enters "(define k (call/cc (lambda (k) k))) 1", then "(k 2)".
> Same question as above.


No. K is a continuation to the definition of a top-level variable at
the REPL -- that's the E part --, and it should proceed to the L part,
which just continues with a new prompt; backtracking on the original
input would be silly.

> 12. The user enters "(define k (call/cc (lambda (k) k)))", then "1", then
> "(k 2)". Same question as above.


Again, no. This and the last scenarios are the same.

> 13. Expressions are read from a file instead of being entered
> interactively - either because the interpreter was invoked differently,
> or because the user used 'load'. I assume that results of individual
> expressions should not be printed (R5RS gives freedom, but this is
> more useful I think). Should definitions be executed while they are
> parsed, or after all are parsed? Should a parse error abort processing
> of the whole file? Should a runtime error abort execution of the whole
> file? Should a continuation include evaluation of the rest of the file?
> Let's assume we don't have a debugger nor program-level catching of
> errors, so the only thing possible is to abort to some point. Or maybe
> some non-R5RS means of catching errors are well-established?


You should provide two operations, loading a script and loading a program.
Loading a script would cause each expression to be evaluated individually, as
if each expression were entered at the REPL; loading a program would behave as
if the program were fully compiled and subsequently run (which is probably what
will indeed happen). Expressions in code loaded as a script should interact
with continuations just as if they were entered individually at the REPL, and
coded loaded as a program should behave as if it were entered at the REPL
surrounded by a BEGIN. Run-time errors in a program should abort the entire
program, or proceed with the rest if the user so signals (with a debugger or
whatever -- I'm not quite sure why a lack of a debugger is assumed) -- again,
as if the whole thing were entered in a BEGIN --, whereas run-time errors in a
script should proceed, regardless of what the user does on the error -- just as
if the user kept entering expressions into the REPL --, though there should be
a way to abort the entire load. This covers most situations that people would
want to use.
Marcin 'Qrczak' Kowalczyk

2004-07-19, 4:01 pm

On Sun, 18 Jul 2004 14:14:54 -0700, Taylor Campbell wrote:

>
> Yes, although I'd personally prefer no prompt.


I realized that a prompt should not prevent copy & paste from a terminal.

OTOH no continuation prompt has a divantage: there is no visual
distinction between a long or infinite computation, and an unfinished
expression (missing close paren).

So I settled with the primary prompt of ">>> " and the continuation
prompt of " ". You can copy & paste multiline expressions, although
it increases indentation if you paste them back at the same REPL.

>
> I think 4 should be printed, and then the REPL should wait. Each
> expression should be evaluated at a time, and, until reached, left in
> the input stream. This allows, for example, for one to send
>
> (read) foo
>
> to the REPL and have it work as expected. Use BEGIN is you really want
> guaranteed sequential expression evaluation (assuming the REPL's
> environment is regular Scheme).


Hmm. I got other voices (by email) in favor of changing it to a more
literal understanding of "read-eval-print". I changed it a bit, but not
totally.

In particular on entering "4 (+ 2" the 4 is now evaluated and printed
immediately, but (read) does not read the rest of the input line. More
importantly, a parse error aborts further processing of the whole line.

I see absolutely no point in responding to

(define (f x) (command1) (command2 #oops) (command3))

with something like

Parse error at "#oo"
Error: undefined variable ps
Parse error at ")"
; The effect of command3 goes here
Parse error at ")"

because the programmer definitely did not indend to execute command3 now.
I will insist that the above would be a side effect of a simplistic
implementation of read-eval-print loop rather than a deliberate behavior
which should be strictly followed.

The actual response is as follows. The parsing engine is an adaptation
of Daan Leijen's Parsec library of monadic parsing combinators for Haskell,
translated into my language Kogut in which the Scheme interpreter is
implemented. Parsec generates error messages semi-automatically from the
grammar.

^
Parse error: unexpected "o";
expected octal digit, "#E", "#I", "#e", "#i", "+", or "-"[color=darkred]

Note that
1. Further input from this line is not processed.
2. The REPL does not simply issue 'read'. The 'read' function uses the
same parser but doesn't show the error with "^" because it doesn't
necessarily read from stdin.
[color=darkred]
>
> No. As above, it should get the result of 1 and then continue.


And this is what happens now, as expressions are evaluated as they are
read. But a read error aborts processing of pending input and gives
a fresh prompt.

>
> What's wrong with ^C?


The language I'm implementing Scheme in doesn't provide catching Unix
signals yet (and I postponed implementing that until I have threads).

I removed this empty line behavior, so you can paste multiline
expressions with empty lines inside them. Now the only method to abort
reading an expression whose partial lines were already entered is to
provoke a parse error.

Of course catching ^C would be desirable, so it can be used to stop a
particular computation. Now it aborts the whole interpreter. Well, it's
a toy.

>
> The user should be thrown into a debugger or something, and whatever it
> is thrown into should receive the 2;


Unfortunately a debugger is too much for a 2100-line interpreter written
in one w.

But anyway, I don't agree that it would be better to throw 2 into it.
While it would be consistent with the literal treatment of read-eval-print
loop, I insist that this literal treatment is a simplification which
doesn't give optimal results, and it's better to not put pending input
into whatever tries to read stdin the next time.

[snip]
The continuation stuff works like you described.

> You should provide two operations, loading a script and loading a program.
> Loading a script would cause each expression to be evaluated individually, as
> if each expression were entered at the REPL; loading a program would behave as
> if the program were fully compiled and subsequently run (which is probably what
> will indeed happen). Expressions in code loaded as a script should interact
> with continuations just as if they were entered individually at the REPL, and
> coded loaded as a program should behave as if it were entered at the REPL
> surrounded by a BEGIN.


I think you are right. But I already implemented a more simplified
approach with only one mode of evaluation: evaluate expressions as they
are read; don't print the results; a read error or evaluation error aborts
interpretation of the whole file; continuations don't include subsequent
definitions. This is used by 'load' and when the interpreter is invoked to
execute a file.

If this interpreter ever grows beyond being a toy, it will include
evaluation of a program as a whole. It is a toy because it doesn't support
anything beyond R5RS, and from R5RS macros and complex numbers are still
missing (and char-ready?, but nothing else).

The goal was to have a nice example of a program written in my language
Kogut, to see how much work is to implement Scheme, perhaps to have a
platform for scripting Kogut programs in future, and to experiment with
styles of foreign language bindings to a high level host language - also
in future.

--
__("< Marcin Kowalczyk
\__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

Grzegorz Chrupała

2004-07-20, 4:00 pm

Marcin 'Qrczak' Kowalczyk wrote:

> The goal was to have a nice example of a program written in my language
> Kogut, to see how much work is to implement Scheme, perhaps to have a
> platform for scripting Kogut programs in future,


So Kogut is unsuitable as a scripting language? I was under the impression
that it was one its design goals.
Cheers,
--
Grzegorz Chrupała | http://pithekos.net | grzegorzc@jabber.org
To describe religions as mind viruses is sometimes interpreted as
contemptuous or even hostile. It is both.
-- Richard Dawkins, A Devil's Chaplain

Marcin 'Qrczak' Kowalczyk

2004-07-20, 4:00 pm

On Tue, 20 Jul 2004 16:36:44 +0200, Grzegorz Chrupa³a wrote:

>
> So Kogut is unsuitable as a scripting language? I was under the impression
> that it was one its design goals.


It doesn't have an interpreter yet, only a compiler.

Well, this reason for having a Scheme was an afterthought :-)

--
__("< Marcin Kowalczyk
\__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com