Most Alice extensions to SML'97 are conservative. There are some incompatibilies with SML'97 however. Most of them are quite pathological, caught at compile time, and can easily be fixed in an SML compatible way. This page describes known deviations and possible (backward-compatible) workarounds.
Also be aware of some limitations of the current version of Alice, which might produce additional incompatibilities.
The following are reserved words in Alice and may not be used as identifiers:
any constructor exttype fct from import non lazy pack spawn unpack withfun withval
Open pulls in infix status. Opening a structure that
will change the infix environment, while in SML it would not.
structure S = struct infix ++ fun l1++l2 = l1 @ l2 end open S val l = ++([1],[2]) (* error: misplaced infix *)
signature SIG = sig val ++ : 'a list * 'a list -> 'a list end structure S : SIG = struct infix ++ fun l1++l2 = l1 @ l2 end open S val l = ++([1],[2])
val l = op++([1],[2])
Recursive value bindings do not remove constructor status on the identifiers bound. You cannot bind functions to an identifier that was a constructor previously.
val rec NONE = fn x => x fun NONE x = x
Both these declarations are legal in SML'97 due to an artefact of the formal language specification and would introduce a function named NONE, hiding the constructor status of NONE. In Alice, they produces a type clash because they are interpreted as trying to match NONE with a lambda expression.
Note: This behaviour is consistent with SML'90 as well as several other SML implementations, and probably what the user would expect.
Alice' structural datatypes come with some restrictions that are not present in SML. In particular, Alice demands that, whenever a datatype constructor is used in an expression or pattern, the corresponding type is visible.
The following program will not type-check:
datatype t = C type t = int val c = C
Though one does not usually write such code, the same situation can appear implicitly through liberal use of open.
For interoperability reasons, Alice currently has the concept of syntactic arity for constructors. For example, in
type t = int * real datatype t = A of int * real | B of {a : bool, b : string} | C of t
constructor A and B both have arity 2, while C has arity 1. Usually, syntactic arity can be ignored. However, signature matching is restricted to disallow changing the syntactic arity of constructors.
Note:The same restriction is imposed by Moscow ML.
Note that syntactic arity is calculated after elimination of derived forms. Therefore C has arity 3 in the following example:
datatype t = C of u withtype u = int * int * string
The following program will not elaborate in the current version of Alice:
signature S1 = sig datatype t = C of int * real end structure M1 :> S1 = (* error: mismatch *) sig type u = int * real datatype t = C of u end
Neither will:
type u = int * real signature S2 = sig datatype t = C of u end structure M2 :> S2 = (* error: mismatch *) sig datatype t = C of int * real end
In most cases, modules can be rewritten by either expanding type synonyms or by introducing auxiliary type synonyms:
structure M1 :> S1 = sig type u = int * real datatype t = C of int * real end structure M2 :> S2 = sig type u = int * real datatype t = C of u end
Transformations can get tedious in the case of higher-order functors, however.
Alice provides higher-order functors. To integrate them smoothly into the language it was necessary to give up the separation between namespaces for structures and those for functors - both are modules. This may break programs that declare structures and functors with identical names.
functor Table() = struct (* ... *) end structure Table = Table() structure Table' = Table() (* error: Table is not a functor *)
Rename your functors or structures appropriately. It is a good idea to stick to naming conventions that denote functors with names like MkTable.
In Alice, datatypes are not generative, but are just structural types similar to records. This has an impact on the use of sharing constraints, which require flexible (i.e. abstract) type constructors.
Alice relaxes the rules for sharing constraints and allows sharing of type constructors as long as one of the following conditions holds:
This makes sharing between datatypes possible in most cases. There are exceptions, however.
signature A = sig type t end signature B = sig datatype t = C end signature S = sig structure A : A structure B : B sharing type A.t = B.t (* error: types incompatible *) end
Signature S will not elaborate because type B.t is specified after A.t and is neither abstract nor identical to A.t (which is abstract).
Signature S can be made valid by reordering structure specifications:
signature S = sig structure B : B structure A : A sharing type A.t = B.t end
Suitable reordering is not always possible, however.
In general, we consider sharing constraints an obsolete feature of SML. Use where constraints instead.
Alice extends many of the structures and signatures defined by the Standard ML Basis Library. This may change the meaning of programs that open library structures, or use library signatures.
In the following snippet, the referenced function equal will be shadowed by the open declaration:
fun equal (a : 'a -> 'b, b : 'a -> 'b) = true open Int val b = equal (op+, op-) (* error: type mismatch *)