You will be more effective if you are aware of the Standard ML Basis Library. To my deep annoyance, different implementations of Standard ML implement different library versions by default. In particular, Moscow ML implements the 1997 basis, whereas MLton implements the 2004 basis. Standard ML of New Jersey's basis depends on the version, but it is no longer provided on the FAS 'nice' servers and we don't recommend it.
In any case, the 1997 basis is used in both the Ramsey and Kamin text and in Ullman, Chapter 9. I therefore recommend that you use this basis. The best guide to the basis is the Moscow ML help system; type
- help "";at the mosml interactive prompt. The script /home/c/s/cs152/bin/mlton-compile runs MLton using this basis.
Do not define axuiliary functions at top level. Use [[local]] or [[let]]. Do not use [[open]]; if needed, use one-letter abbreviations for common structures. Do not use any imperative features unless the problem explicitly says it is OK.
Feel free to use the standard basis extensively. (But beware that the documentation at standardml.org may not be consistent with your implementation.) Moscow ML's [[help "lib";]] will tell you all about the library. And if you use
mosml -P fullas your interactive top-level loop, it will automatically load almost everything you might want from the standard basis.
All the sample code we show you is gathered in one place online.
As you start to learn ML, this table may help you convert your current knowledge:
Put all your solutions in one file: warmup.sml. (If separate files are easier, combine them with cat.) To receive credit, your warmup.sml file must compile and execute in the Moscow ML system. For example, we must be able to compile your code without warnings or errors:
μScheme ML val val define fun lambda fn
ice3 /tmp >> /home/c/s/cs152/bin/mosmlc -c warmup.sml ice3 /tmp >>Please remember to put your name, userid, and time spent in the warmup.sml file.
compound : ('a * 'a -> 'a) -> int -> 'a -> 'athat ``compounds'' a binary operator [[rator]] so that [[compound rator n x]] is [[x]] if [[n=0]], [[x rator x]] if [[n = 1]], and in general [[x rator (x rator (... rator x))]] where [[rator]] is applied exactly [[n]] times. [[compound rator]] need not behave well when applied to negative integers.
pow : int -> int -> intso that, for example, [[pow 3 2]] evaluates to 9. Hint: take note of the description of [[op]] in Ullman S5.4.4, page 165.
Don't get confused by infix vs prefix operators. Remember this:
('a * 'b -> 'b) -> 'b -> 'a list -> 'bThey are like the μScheme versions except the ML versions are Curried.
pairfoldr : ('a * 'b * 'c -> 'c) -> 'c -> 'a list * 'b list -> 'cthat applies a three-argument function to a pair of lists of equal length, using the same order as [[foldr]]. Use [[pairfoldr]] to implement [[zip]].
We can use the [[order]] idiom to define a higher-order insertion function by, e.g.,
<
We can use this idea to implement polymorphic sets in which we store the comparison
function in the set itself.
For example,
<
The function [[setFold]] should visit every element of the set exactly once, in an unspecified order.
Consider the class of well-formed arithmetic computations using the numeral 5. These are expressions formed by taking the integer literal 5, the four arithmetic operators +, -, *, and /, and properly placed parentheses. Such expressions correspond to binary trees in which the internal nodes are operators and every leaf is a 5. Write a μScheme program to answer one or more of the following questions:Write an ML function [[reachable]] of type
- What is the smallest positive integer than cannot be computed by an expression involving exactly five 5's?
- What is the largest prime number that can computed by an expression involving exactly five 5's?
- Exhibit an expression that evaluates to that prime number.
('a * 'a -> order) * ('a * 'a -> 'a) list -> 'a -> int -> 'a setsuch that [[reachable (Int.compare, [op +, op -, op *, op div]) 5 5]] computes the set of all integers computable using the given operators and exactly five 5's. (You don't have to bother giving the answers to the questions above, since they're easy to get with [[setFold]].) My solution is under 20 lines of code, but it makes heavy use of the [[setFold]], [[nullset]], [[addelt]], and [[pairfoldr]] functions defined earlier.
Hints:
fun reachable (cmp, operators) five n = (* produce set of expressions reachable with exactly n fives *)
-> (val f (lambda (x y ...)) (+ x (+ x (foldl + 0 ...))) -> (f 1 2 3 4 5) ; inside f, rho = { x |-> 1, y |->, ... |-> '(3 4 5) } 15In this example, it is an error for [[f]] to get fewer than two arguments. If [[f]] gets at least two arguments, any additional arguments are placed into an ordinarily list, and the list is used to initialize the location of the formal parameteter associated with [[...]].
and lambda = name list * { varargs : bool } * expThe type system will tell you what other code you have to change. For the parser, you may find the following function useful:
fun newLambda (formals, body) = case rev formals of "..." :: fs' => LAMBDA (rev fs', {varargs=true}, body) | _ => LAMBDA (formals, {varargs=false}, body)The type of this function is
[[name list * exp -> name list * {varargs : bool} * exp]];thus it is designed exactly for you to adapt old syntax to new syntax; you just drop it into the parser wherever [[LAMBDA]] was used.
[[(call f '(1 2 3))]]is equivalent to
[[(f 1 2 3)]]Sadly, you won't be able to use [[PRIMITIVE]] for this; you'll have to invent a new kind of thing that has access to the internal [[eval]].
-> (val cl (cons-logger)) -> (val log-cons (car cl)) -> (val conses-logged (cdr cl)) -> (conses-logged) 0 -> (log-cons f e1 e2 ... en) ; returns (f e1 e2 ... en), incrementing ; private counter whenever cons is called -> (conses-logged) 99 ; or whatever else is the number of times cons is called ; during the call to log-cons