From lliao@ren.eecis.udel.edu Sat Oct 5 23:02:23 2002 Date: Sat, 5 Oct 2002 23:01:06 -0400 (EDT) From: Li Liao To: Natasha Duncan Subject: Re: write function > in one of our exec cases we have to define the "write case". > It has to print out the first number on the stack and no changes > on the stack are done, so we have to return (vs,ds) back. That is correct. > how do we execute/call both functions in that case - one that returns > (vs,ds) and the print function that returns unit. The compiler will > complain that I'm not returning the right type. As this is peripheral to the core of this assignment (which is about names and scoping), I decided to give you the code about write/read cases. fun ... | exec Pop (v::vs, ds) = (vs, ds) | exec Write (n::vs, ds) = (writeNum (getNum n); (vs, ds)) | exec Read (vs, ds) = (Num (readNum ())::vs, ds) | ... where getNum is a helper function defined as fun getNum (Num n) = n | getNum _ => raise TypeError Let me explain the code a bit so that you can understand how it works. As we reiterated, expressions in functional languages are evaluated to return values. This is in contrast to commands in imperative languages; commands are executed to change the machine's states-- the so-called side effect. SML is not purely functional; it contains imperative programming features, particularly for input and output. The print command has the syntax similar to that of language C: print ("hollow world\n") When evaluated (or executed), its effect is to print a string to the screen. Nonetheless, SML assigns a value () as the return of any command. The value () has a special type called "unit". Therefore, the print command has a type: string -> unit. A series of commands can be executed by the expression (E1; E2; ...; En) When this expression is evaluated, the expressions E1, E2, ..., En are evaluated from left to right. The result is the value of En; the values of the other expressions are discarded. Because of the other uses of the semicolon in ML, this construct must always be enclosed in parentheses. This explains the magic effect of ";". If you are not aware of this magic effect, you still can get over the type error message from the compiler, as is done in the following code. fun ... | exec Write (v::vs, ds) = (case v of (Num n) => ( case writeNum (n) of (unit) => (vs, ds)) | _ => raise TypeError ) |... The trick is to use pattern matching to catch the value returned from the command writeNum in order for that vaule not to interfere with the value (vs, ds), which is the expected for the function "exec". I hope this is clear and helpful. Li