'a -> 'a
int * 'a -> 'a
'a * 'b * 'c -> 'c * 'b * 'a
'a list -> 'a
unit -> 'a
Consider the following datatype representing "ground" formulas in propositional logic (with no variables):
datatype GForm = GConst of bool | GNot of GForm | GOr of GForm * GForm | GAnd of GForm * GForm
For example, the formula true and (false or not true) would be represented by the ML data structure
GAnd(GConst true, GOr(GConst false, GNot (GConst true)))
evalGForm
of type GForm
-> bool
, which evaluates ground propositional
formulas.Now, consider propositional formulas with variables:
datatype Formula = Const of bool | Var of string | Not of Formula | Or of Formula * Formula | And of Formula * Formula
Since we have variables, formulas can only be evaluated in an environment, which maps variables to values:
type Environment = (string * bool) list
Fundamentally, when one has variables in any language, whether propositional logic or a more complicated programming language, there are two ways to implement evaluation. First, one could pass the environment recursively through each evaluation step, and lookup variables on demand. (This is the approach we'll take in the Mini-ML evaluator.) The second approach is to substitute the actual value of each variable consistently throughout the expression, and then evaluate the resulting "ground" expression.
substVar
, having type
Environment * Formula -> GForm
, which translates a
Formula
into the GForm
that is equivalent
to it in the given Environment
. Use higher-order
functions (like map
, find
, etc.) to
manipulate the environment, to the extent possible and
appropriate.In ML, you can simulate objects to some extent by using records of functions. Consider the following type for "functional points":
type Point = { x:unit -> real, y:unit -> real, translate:real * real -> Point, rotate :real -> Point }
where x
returns the x-coordinate, y
returns the y-coordinate, translate
returns a new point
that is like the old point, but moved by the new x and y coordinates
(i.e., if you take the point (-12, 3.1) and translate it by (2.5,
4.2), you get (-9.5, 7.3)), and rotate
rotates the point
about the origin by the specified number of radians.
Notice that the operations that would mutate a point in a traditional OO language instead return a copy of a new point. Hence, you could write the following code:
val p:Point = ...; (* gets x value of p *) val x_of_p = (#x p)(); (* get a point like p, but rotated 180 degrees *) val rotated_p = (#rotate p)(Math.PI);
Aside from the rather ugly syntax, we can use the record members as we would use message names (a.k.a. method names in Java, or virtual function names in C++) in object-oriented language. As in OO languages, the client is insulated from the implementation of the functions that implement these messages.
Point
type declaration into ML and try
to evaluate it. Explain the error message you get. What feature,
which ML type synonyms lack, do you need here?Point
type that fixes this problem. You may
use any ML construct you know.makeRect:real * real -> Point
,
which takes x
and y
values and returns a
Point
with the given x
and y
values. This object should have an efficient translate
implementation (two arithmetic operations).makePolar:real * real -> Point
,
which takes rho
and theta
values and returns
a Point
. The object resulting from a
makePolar
call should have an efficient
rotate
implementation (one arithmetic operation).ColoredPoint
type, which is exactly like
Point
but contains an additional string field describing
its color.Point
and
ColoredPoint
type objects? Why not? What error does ML
give you when you try?