module
system") ?>

Alice extends the SML module system in various ways, by providing:

Items marked with (*) are not fully implemented in Operette 1.

The last item includes allowing op in signatures, having a withtype derived form in signatures (analoguous to declarations), and allowing redundant parentheses in structure and signature expressions.

A syntax summary is given below.

Alice discards SML's separation between core declarations (dec), structure declarations (strdec), and toplevel declarations (topdec). As a consequence, structures can be declared local to an expression (via let) and functors as well as signatures can be nested into structures. For example:

	fun sortIntList ns =
	    let
	        structure Tree = MkBinTree(type t = int)
	    in
		Tree.toList(foldl Tree.insert Tree.empty ns)
	    end
  

A direct consequence of allowing functors everywhere is the presence of higher-order functors, even if a bit cumbersome:

	functor F(X:S1) =
	    struct
	        functor G(Y:S2) = struct (* ... *) end
	    end

	structure M = let structure Z = F(X) in G(Y) end
  

This is exactly how SML/NJ introduces higher-order functors. Alice provides higher-order functors in a more first-class fashion. The above example can be written more directly as:

	functor F(X:S1)(Y:S2) = struct (* ... *) end

	structure M = F(X)(Y)
  

Alice has real functor expressions. Similar to fun declarations, functor declarations are mere derived forms. The declaration for F above is just sugar for:

	structure F = fct(X:S1) => fct(Y:S2) => struct (* ... *) end
  

The keyword fct starts a functor expression very much like fn begins a function expression in the core language. Functor expressions can be arbitrarily mixed with other structure expressions. In contrast to SML, there is no distinction between structure and functor identifiers (see incompatibilities).

(Note: We decided to keep the keyword structure - and the syntactic classes strid, strexp, etc. - for compatibility reasons, although module might now be more appropriate.)

The syntax for signatures has been extended to contain functor types. For example, functor F can be described by the following signature:

	structure F : fct(X:S1) -> fct(Y:S2) -> sig (* ... *) end
  

As a comfortable derived form the following SML/NJ compatible syntax is provided for functor descriptions in signatures:

	functor F(X:S1)(Y:S2) : sig (* ... *) end
  

For completeness of the lattice of signature types we also introduced a top signature, denoted by the keyword any:

	functor Id(X: any) = X
  

Like structures and functors, signatures can also be declared anywhere. In particular, this allows signatures inside structures, and consequently, nested signatures:

	signature S =
	    sig
	        signature T = sig (* ... *) end
	    end

	structure X :> S =
	    struct
	        signature T = sig (* ... *) end
	    end
  

Such manifest signatures must always be matched exactly (Note: matching of manifest signatures is not checked yet). However, analoguous to types, we allow for abstract signature members:

	signature S =
	    sig
	        signature T
	        structure X : T
	    end
  

Note that this feature renders the type system of Alice undecidable (the same is true for O'Caml, which has a very similar module language). We do not consider this a problem in practice, however, since the simplest program to make the type checker loop already is highly artificial:

	signature I =
	    sig
	        signature A
	        functor F(X : sig
	                          signature A = A
	                          functor F(X : A) : sig end
	                      end) : sig end
	    end

	signature J =
	    sig
	        signature A = I
	        functor F(X : I) : sig end
	    end

	(* Try to check J <= I *)

	functor Loop(X : J) = X : I
  

Currently the compiler has no upper limit on the number of substitutions it does during signature matching, so this example will actually make it loop until memory is exhausted.

Functors often require putting where constraints on signatures to denote exact return types. This can become quite tedious. Alice provides an alternative by generalizing signature identifiers to signature constructors, parameterized over structure values:

	signature SET(Elem : sig type t end) =
	    sig
	        type elem = Elem.t
		type t
		(* ... *)
	    end

	functor MakeSet(Elem : sig type t end) :> SET(Elem) =
	    struct
		(* ... *)
	    end
  

The same derived forms as for functor declarations apply, so SET and MakeSet can be defined as:

	signature SET(type t) =
	    sig
	        type elem = t
		type t
		(* ... *)
	    end

	functor MakeSet(type t) :> SET(type t = t) =
	    struct
		(* ... *)
	    end
  

Caveat: parameterized signatures are not yet properly treated in Operette 1.

The module system can be abused to type some more delicate operations, like pickling:

	signature UNIT = sig end

	functor Pickle(type t  val x : t) : UNIT
  

When utilizing modules for this purpose the result of a functor application is uninteresting. Similar to core declarations, wildcards are thus provided as a derived form:

	structure _ = Pickle(type t = int  val x = 43)
  

Similarly, wildcards are allowed for functor parameters:

	functor F(_ : S) = struct (* don't actually need argument *) end
  

This is particularly useful in signatures:

	signature FF = fct(_ : A) -> B
  

Signatures can contain fixity specifications:

	signature S =
	    sig
	        type t
	        infix 2 ++
		val x :    t
	        val op++ : t * t -> t
	    end
  

To match a signature with infix specifications, a structure must provide the same infix status directives. The infix environment is part of a structures principal signature.

Opening a structure with unconstrained infix members pulls in the according infix status in the local environment:

	structure M :> S = struct (* ... *) end

	open M
	val z = x ++ x
  

Note that this feature produces a syntactic incompatibility with SML showing up in some rare cases.

The syntax for modules very much resembles the syntax of core language expressions:

Structures

atstrexp ::= struct dec end structure
longstrid structure identifier
let dec in strexp end local declarations
( strexp ) parentheses
appstrexp ::= atstrexp
appstrexp atstrexp functor application
strexp ::= appstrexp
strexp : sigexp transparent constraint
strexp :> sigexp opaque constraint
fct strpat => strexp functor
strpat ::= ( strid : sigexp )

Signatures

atsigexp ::= any top
sig spec end ground signature
longsigid signature identifier
( sigexp ) parentheses
appsigexp ::= atsigexp
appsigexp atstrexp signature application
sigexp ::= appsigexp
fct strpat -> sigexp functor
sigexp where rea specialization
rea ::= type tyvarseq longtycon = ty
signature longsigid strpat1 ... strpatn = sigexp (n>=0)
sigbind ::= sigid strpat1 ... strpatn = sigexp <and sigbind> (n>=0)
sigdesc ::= sigid strpat1 ... strpatn <= sigexp> <and sigdesc> (n>=0)

Derived Forms

atstrexp ::= ( dec )
strpat ::= ( spec )
( _ : sigexp )
dec ::= functor fstrbind
strbind ::= _ <: sigexp> = strexp <and strbind>
fstrbind ::= strid strpat1 ... strpatn = strexp <and fstrbind> (n>=1)
spec ::= functor fstrdesc
fstrdesc ::= strid strpat1 ... strpatn : sigexp <and fstrdesc> (n>=1)