On 20 Sep 2012, at 13:48, David Matthews wrote:
On 20/09/2012 08:20, Lars-Henrik Eriksson wrote:
19 sep 2012 kl. 18.00 skrev David Matthews:
Also the same ML code *will* behave differently when run interactively or stand-alone using Poly/ML:
Poly/ML 5.5.0 Release
fun f() = (print "Say something: "; TextIO.inputLine TextIO.stdIn; print "Thank you.\n");
val f = fn: unit -> unit
PolyML.export("test",f);
val it = (): unit
f();Hello
Say something: Thank you. val it = (): unit
unix> ./test Say something: Hello Thank you.
I believe that the stand-alone behavior is the reasonable one -- it also agrees with SML/NJ and Moscow ML.
If you remember that the read-eval-print loop uses TextIO.input1 rather than TextIO.inputLine it all makes sense. At the end of compiling the next character to be read from stdIn is the character immediately after the closing semicolon. That may or may not be a newline character depending on what the rest of the input was. If the code that has just been compiled wants to read from the stream this is where it starts. The next time round the loop the compiler starts after anything that has been removed. When running as a stand-alone executable the behaviour is exactly the same; it's just that the compiler has not been run so the function starts at the beginning of the stream.
Does this cause problems? Yes, for me it does. We use ML in the introductory programming course for CS majors and it causes unnecessary complications for the students.
I am not completely wedded to the present way of doing things but I feel there has to be a stronger rationale for changing it. I would be interested to know if anyone else has feelings one way or the other.
I have fairly strong feelings that it is not worth worrying about. Personally, I find the Poly/ML behaviour more to my liking than the SML/NJ behaviour because beginning execution immediately after reading the semicolon feels more intuitive. Programmers and language implementors are all doomed if the rest of the line containing the i/o command is not syntactically complete, as in:
print "Type something, please: "; val x = TextIO.inputLine TextIO.stdIn; print ( "thank you!");
(Poly/ML and SML/NJ both bind something to x and then report a syntax error, but the value of x and the syntax errors are different: in Poly/ML, the left bracket has been consumed into x and the right bracket is unmatched, but vice versa for SML/NJ.)
In any case, isn't this an issue for the implementation of the basis library rather than the compiler proper? I don't see any reason why the interactive compiler and the basis library need to share their standard i/o streams. That's what happens with the existing implementations, but I don't see that the basis library specification mandates it - couldn't a valid implementation of the basis library bring up a separate window for its standard i/o?
So I think I side with Larry Paulson in viewing this as an instructive instance where the programmer needs to distinguish between implementation-dependent behaviour and behaviour that is guaranteed by the relevant specifications or standards.
Regards,
Rob.