A number of methods of combining polymorphic definition and imperative
features were proposed by Mads Tofte [Tof90] and
Standard ML adopts the simplest of them. A persuasive argument is
provided by Andrew Wright [Wri95] that relatively
little expressiveness is lost in making this choice. Tofte's approach
divides expressions into the categories of
*expansive* and
*non-expansive*. The essence of
the idea is that only non-expansive expressions may be polymorphic.

What is a non-expansive expression? We may characterise it as a value and thus Standard ML is said to have `value polymorphism'. More precisely, a non-expansive expression is

- a constant;
- an identifier;
- a record where the labels are associated with non-expansive expressions;
- an application of any constructor except ref to a non-expansive expression; and
- any
**fn**expression

The following value declarations are not permitted since they contain expansive expressions of polymorphic type.

(** All rejected by the compiler **)

valr = rev [];valr = ref [];valr = ref (fnx => x);valconcat = foldr (op@) [];

Notice that the value polymorphism applies even for purely functional
programs which make no use of the imperative features of the language.
When confronted with a polymorphic expression which is rejected
because it contains a free type variable (at the top level) there
are several possible solutions. One is to simplify the expression to
remove any unneeded function applications--such as replacing
rev [] with []--and another solution is to add an
explicit type constraint if the desired result is monomorphic. For
expressions which denote polymorphic functions the introduction of an
explicit **fn** abstraction solves the problem. For example,
concat can be written thus

valconcat =fns => foldr (op@) [] s;

or using the derived form for this which we saw [*].

The following value declarations *are* legal because their
(monomorphic) types can be determined.

letvalr = rev []invals = implode rend;letvalx = ref []invals = 1 :: !xend;

The following imperative version of the rev function
[*] contains only occurrences of expansive
expressions within the body of a **fn** abstraction (recall that
the **fun** keyword is a derived form which includes a **fn**
abstraction). This function reverses lists in linear time and is
thus called fastrev.

funfastrev l =letvalleft = ref landright = ref []inwhilenot (List.null (!left))do(right := hd (!left) :: !right; left := tl (!left)); !rightend;

The Standard ML system will compute the type 'a list -> 'a list for this function, just as it did for the functional version of list reversal.