SML does not deal with separate compilation. Alice repairs this by adding a component system to the language. Alice components were inspired by Mozart "functors". They support lazy linking and loading from remote URLs, but add static and dynamic type checking. Components are architecture independent and can be executed on any system.
A component is an SML program headed by a set of import announcements of the form:
import imp from "url"
The URL describes where to find the component, while the import imp denotes what kind of item is imported from that particular component. For example,
import structure Foo from "http://ps.uni-sb.de/stockhausen/Foo.xtc"
An import can contain any language entity, like values, types, structures, signatures, etc. Imports can be given in plain form like above (in which case the compiler looks up the actual type of the entity in the component itself) or in description form similar to specifications (in which case the compiler verifies that the description matches the actual component):
import signature FOO from "http://ps.uni-sb.de/stockhausen/FOO.xtc" import structure Foo : FOO from "http://ps.uni-sb.de/stockhausen/Foo.xtc"
Each component exports exactly its body environment, i.e. all entities declared on its toplevel. For example, the component
import structure Y from "other.xtc" signature S = sig end structure X :> S = struct end
enables other components to import a signature S and a structure X from it. Structure Y is not exported (but can be by a simple rebinding, of course).
A Stockhausen program is executed by starting a component. In general, a component relies on other components. These other components are loaded and evaluated by need by the Komponist, who is part of the runtime system. A component that is never actually accessed at runtime will not get loaded. URL resolving is handled similar to Mozart.
When a component is loaded, its signature is matched against the signature expected by the dependent component - the signature that was found when the dependent component was compiled. A runtime exception is raised when the signature does not match. If the check was successful, the component is executed in a separate thread.
Every component is loaded and executed at most once in a single process. If several other components load a component from the same URL, they will share a reference to the same instantiation of that component.
| component | ::= | ann <program> | component |
| ann | ::= | import imp from string | import announcement |
| empty | |||
| ann <;> ann | |||
| imp | ::= | val valitem | |
| type typitem | |||
| datatype datitem | |||
| constructor dconitem | |||
| exception exitem | |||
| structure stritem | |||
| functor funitem | |||
| signature sigitem | |||
| infix <d> vid1 ... vidn | (n>=1) | ||
| infixr <d> vid1 ... vidn | (n>=1) | ||
| nonfix vid1 ... vidn | (n>=1) | ||
| empty | |||
| imp <;> imp | |||
| valitem | ::= | <op> vid <and valitem> | |
| <op> vid : ty <and valitem> | |||
| typitem | ::= | tycon <and typitem> | |
| tyvarseq tycon <and typitem> | |||
| datitem | ::= | tycon <and datitem> | |
| tyvarseq tycon = conitem <and datitem> | |||
| conitem | ::= | <op> vid <of ty> <| conitem> | |
| dconitem | ::= | <op> vid <and dconitem> | |
| <op> vid <of ty> : tyvarseq longtycon <and dconitem> | |||
| exitem | ::= | <op> vid <and exitem> | |
| <op> vid of ty <and exitem> | |||
| stritem | ::= | strid <and stritem> | |
| strid : sigexp <and stritem> | |||
| funitem | ::= | strid <and funitem> | |
| strid strpat1 ... strpatn : sigexp <and funitem> | (n>=1) | ||
| sigitem | ::= | sigid <and sigitem> | |
| sigid strpat1 ... strpatn <and sigitem> | (n>=1) |
Note that exception and functor imports are actually derived forms.