Types
Intrinsic Types
These types are not defined anywhere you can read. They’re just built into the lowest levels of the language.
flagThe Boolean truth values are called
yesandno.numberIn the Python-hosted implementation,
numberis anything Python treats as a number, even including complex numbers. The future bytecoded implementation will probably go with all double-precision floating point until there’s a demonstrated need for the semantics associated with binary integers.stringThese are unicode. Or perhaps UTF8-encoded on the VM. We’ll see. At some point I’ll end up distinguishing text from binary data. That day is not today.
Predefined Types
These types are defined in the standard preamble. They may as well be built in, for all anyone cares.
listHas two cases:
nilis the empty list, whileconshas fieldsheadandtail. The implementation of explicit lists like[this, that, the, other]is in terms ofcons.maybeCan be either
this(item)ornope. Useful return type for functions that don’t always have a meaningful answer. For example, a search function might not find what it’s looking for. It can returnmaybe[found]for some typefound.orderConsists of the enumeration
less,same,more. The purpose is a return-value type for functions that compare totally-ordered things. Eventually a spaceship operator<?>will return this type once operator overloading drops.pairA simple record consisting of fields
fstandsnd. There is no required relation between their types.ConsoleThe
roleof the built-inconsoleactor. It defines methodsecho,read, andrandom. Eventually it will deserve a section of its own.
And soon:
FileSystemThe
roleof the built-infilesystemactor. This is presently in a separate module, but I plan to make it part of the standard preamble.
Types You Compose
You can express a variety of compound types anywhere in a type signature.
- Types by Name
flagandlistare both perfectly good type names. In this case,listis generic: You’ve not specified what sort of item the list contains.- Names Made Concrete
list[number]says that the list is specifically a list of numbers.pair[apple, orange]suggests that you won’t be giving a meaningful comparison between.fstand.snd.- Functions
Use an arrow and parenthesis to compose a function type. For example,
(string, number, number) -> stringdescribes a function that converts a string and two numbers to a new string.- Message-handler Types
Use the exclamation point and parenthesis to compose a message-handler type. (This can refer either to a plain procedure or a method bound to an actor.) For example,
!(list[string]))is the type of a message-handler that takes a list of strings. The type!is that of a message-handler that takes no parameters.- The Don’t-Care Type
You can use a question mark to indicate a type you don’t care about.
pair[?, flag]says the.sndelement of the pair must be aflag, but we leave unspecified what type the.fstis.- Ad-Hoc Type Variables
While defining actual functions, you may optionally annotate the parameter types and return type. If these types are generic, you can either use
?to let Sophie figure it out, or your can specifically tie type positions together with?xwherexcan be any otherwise-unused type name. For example:define: map( fn:(?a)->?b, xs:list[?a] ) : list[?b] =
This declares that function
map… does exactly what it does, at least in the domain of types. (The rest of the implementation is left as an exercise, or you can look in the standard preamble.)
Types You Define
To define your own types, include a type: section in your module.
- Record Types
For example:
my_record is (field_1:type_1, field_2:type_2);
You can have anywhere from 1 to 255 fields. Each field must declare its type, even if this seems redundant.
The word
my_recordadditionally becomes a constructor, which behaves like a function that produces records of typemy_record.- Generic Types
After the name of a type, include type-variables in square brackets. For example:
my_generic_record[X] is (alpha:X, beta:X, gamma:number);
Now you can make any
my_generic_recordyou like, as long as itsalphaandbetafields have the same type as each other.Entry[K, V] is (key:K, value:V);
You can have as many type parameters as you need.
- Tagged Variants
For example:
list[x] is CASE: nil; cons(head:x, tail:list[x]); ESAC;This is the actual definition of
listin the standard preamble. Because thetailhas typelist[x], this means by induction that a list contains all the same type of elements.You can have up to 255 cases. Each case must either be a record or, as in the case of
nil, just an identifier. In this casenilis a constant value standing for itself, andconsis a two-argument constructor.Notice that
nildoes not mention parameterx. Therefore, thenilvalue is interchangeable among different kinds of lists. However, you’re unlikely to find inventive uses for this fact.You can work with the specific components of a tagged-variant using
CASE…OFsyntax:map(fn, xs) = case xs of nil -> nil; cons -> cons(fn(xs.head), map(fn, xs.tail)); esac;- Alias Types
You can give a name to any simple type. For example,
predicate[x] is (x)->flaggives the namepredicateto mean a function (of one argument) that returns aflag. Alias types do not make constructors.- Role Types
This gives a type which some actor can implement. At the moment, it’s mostly useful with the foreign function interface, because actor definitions in the
define:section implicitly define their own type. Example:Console is role: echo(list[string]); read(!(string)); random(!(number)); end;This says that anything satisfying the
Consoleinterface can accept the three messagesecho,read, andrandom, with message signatures as given.