The current Alice system is based on the Mozart Programming System and supports interoperability between Oz and Alice.
The Alice compiler translates components into pickled Oz functors. It is possible to mix Oz and Alice code on a per-component basis. This document explains:
Values of basic types are mapped to corresponding Oz basic types. To support word operations, Mozart has been extended by a corresponding basic type.
Alice Type | Oz Representation |
---|---|
int | Infinite-precision integer |
char | Infinite-precision integer |
word | Provided by boot module x-oz://boot/Word |
real | Float |
string | Byte string |
Alice records are translated to Oz records by mapping all labels consisting only of digits to the corresponding integer feature, and all labels containing a letter to the corresponding atom feature.
Alice Type | Oz Representation |
---|---|
Unit value () resp. {} | Literal unit |
Tuple | Tuple with label '#' |
Record | Record with label '#' |
Conceptually, all Alice functions are unary. The typical idiom is to pass multiple arguments as a tuple of arguments. This case is treated specially by the Alice compiler.
Alice Procedure | Oz Representation |
---|---|
Function in which every pattern is either an explicit n-tuple or a wildcard | n+1-ary procedure, having n input arguments and one output argument |
Any other function | Binary procedure |
When applying functions in Alice, a calling convention conversion is performed: If a single-argument function is applied to several arguments (that is, a manifest tuple), these arguments are tupled to form a single argument. Conversely, when a multiple-argument function (that is, a function expecting a manifest tuple) is applied to a single argument, it is deconstructed as a tuple.
Note that care must be taken when calling a first-class function defined in Alice from an Oz procedure: calling procedures from Oz does not perform calling convention conversion automatically. Therefore, the user must write code to do this explicitly using the Procedure.arity and Procedure.apply builtins. For instance, if F is to be called with three arguments, but may have been defined in Alice:
X = case {Procedure.arity F} of 4 then {F A B C} % call with three arguments directly [] 2 then {F A#B#C} % call with one (constructed) argument end
Note that a three-argument Alice function is a four-argument Oz procedure (because of the return value), and that a one-argument Alice function is a two-argument Oz procedure.
With a few exceptions simplifying interoperability, constructors of closed datatypes are mapped to atoms and constructors of extensible datatypes are mapped to names.
Alice Type | Oz Representation |
---|---|
true, false | Literals true, false |
::, nil | '|', nil |
Other constructors of closed datatypes | Atoms with corresponding print name |
Constructors of extensible datatypes | Names |
Conceptually, all Alice constructors are unary. For interoperability, constructors syntactically declared taking a record as argument are treated as n-ary constructors.
Alice Constructed Value | Oz Representation |
---|---|
Constructed value of an n-ary constructor, n > 0 | Record with the literal corresponding to the constructor as label and the argument record's labels as features |
ref x | {NewCell x} |
For example, assuming a declaration
datatype t1 = C1 of int * int
the Alice expression C1 (1, 2) evaluates to the Oz value 'C1'(1 2) due to the constructor C1 being binary. However, assuming the declaration
datatype 'a t2 = C2 of 'a
the Alice expression C2 (1, 2) evaluates to the Oz value 'C2'(1#2) because the constructor C2 is unary.
Alice futures map directly to Oz transients. Promises are transparent on the Oz side.
Alice Future | Oz Representation |
---|---|
Future | Future |
By-need future | By-need future |
Promise | Record with label 'Promise__' and two subtrees, a
boolean cell and a logic variable. The cell is true if the promise has either been fulfilled or failed |
Some abstract library types are implemented natively:
Alice Library Type | Oz Representation |
---|---|
ref | Cell |
array | Array |
vector | Tuple with label '#[]' |
Thread.thread | Thread |
Atom.t | Atom |
Alice modules are translated to Oz data structures such:
Alice Module | Oz Representation |
---|---|
Structure | Record |
Functor | Binary procedure |
Structure members are represented under record features. The feature names are computed as shown in the following table:
Alice Identifier Class | Oz Feature Representation |
---|---|
Value or constructor, e.g., val x, constructor C | Identifier name as atom, e.g., 'x', 'C' |
Type, e.g., datatype t | Dollar-prefixed identifier as atom, e.g., '$t' |
Structure or functor, e.g., structure M | Dollar-suffixed identifier as atom, e.g., 'M$' |
Signature, e.g., signature S | Dollar-pre- and -suffixed identifier as atom,
e.g., '$S$' |
Alice components are translated into Oz functors:
Alice Component | Oz Representation |
---|---|
Compiled component | Pickled functor |
The export of the generated functor is the record corresponding to the component considered as a single structure.
Alice exceptions are wrapped into alice(...) and raised as Oz error exceptions.
Alice compiled components are annotated with signatures denoting the expected signatures of the component it imports and the actual signature of the structure it computes. Compatibility of actual and expected signatures is checked at link-time to guarantee type safety. The actual export signature is also loaded at compile time to perform binding analysis and type-checking.
For this reason, export signatures must first be provided for Oz functors before they can be imported into Alice components. This will be shown by an example.
Assume the following Oz functor is stored at URL F.ozf:
functor import System(show) export show: Show define fun {Show X} {System.show X} unit end end
To import this component into Alice, we would write a signature file F.asig containing:
signature F_COMPONENT = sig val show: 'a -> unit end
We can now import the component into Alice using an import announcement of the form
import val show from "F"
This will read the signature from F.asig, and consider the structure exported from F.ozf to conform to this signature. (Note that the name of the signature is ignored.) If the signature does not truthfully describe the Oz component, run-time errors will occur.
Since Oz does not type-check its imports at link-time, Alice components can be directly imported into Oz functors without conversion.
For example, assume the following Alice component is available at URL Example.ozf:
structure Example = struct fun count f xs = List.foldr (fn (x, n) => if f x then n + 1 else n) 0 xs end
An Oz functor could now import it as follows:
functor import System(show) ExampleComponent('Example$': Example) at 'Example.ozf' define {System.show {{Example.count fun {$ X} X == 0 end} [1 0 2 0]}} end