Along the lines of row polymorphism in OCaml and Elm. We should be able to specify concrete record types up front but also create them in an ad-hoc manner. Addition and removal of record members is not something I’m planning on including at the moment.
Up front:
type rec1 = {x: float, y: int, z: atom} type rec2 = {x: float, y: int} -- call this with any mix of rec1 and rec2: add_two_xs a b = match (a, b) with ({x = x1}, {x = x2}) -> x1 + x2
Or ad-hoc:
add_two_xs a b = match (a, b) with ({x = x1}, {x = x2}) -> x1 + x2 {- Results in the integer `3` without any specific record types defined prior to this call: -} add_two_xs {x=2, y=3} {x=1}
Compiling records as maps probably is easiest but in talking with others there seems to be a case (speed, natural ordering) for compiling to tuples at the expense of more compiler-generated code. This precludes binary dependencies as the compiler must be able to build record handling code for any records sent from a user’s program to a dependency that expects records.
A compiler switch to optionally compile to maps might be helpful for those concerned with hot code reloading.
Initial compilation support is targetting only maps as this is the fastest path to deliver something we can actually try out.
Still TODO:
- field punning,
{x, y}
in a match instead of{x=x1, y=y1}
so we don’t have to bind completely separate variable names when there’s no overlap. - field access, e.g.
foo.x
to access thex
member instead of needing a pattern match to get to it. - compilation to tuples. This will require keeping a global record of all explicit (and distinct) record instantiations from each module so that we can generate every pattern required.
It would be good if we could partially specify the types of functions, leaving it up to the inferencer to fill in the blanks. There are two options at the moment:
- types as part of documentation
- separate type specifications (somewhat like Haskell)
I currently prefer the former so that documentation correctness has the capability to be somewhat enforced.
Use doc strings to specify types so that the correctness of documentation is enforced:
type even_odd = Even int | Odd int {- Determine if @x[int] is even or odd. -} f x = match x % 2 with 0 -> Even x | _ -> Odd x
In the above example, we specify that x
must be an integer and leave it up to the inferencer to figure out the return type. The following change should fail to type-check and thus not compile:
type even_odd = Even int | Odd int {- Determine if @x[float] is even or odd. Because @x must be an integer to work with the modulo operator (%), this documentation string and the method are at odds so this should fail to type. -} f x = match x % 2 with 0 -> Even x | _ -> Odd x
Fully specified, something like the following:
type even_odd = Even int | Odd int {- Determine if @x[int] is even or odd. @return[even_odd] -} f x = match x % 2 with 0 -> Even x | _ -> Odd x
This approach requires the full integration of comments into the AST.
Alternatively we could use fairly typical specifications that still allow for partially specifying types when it’s convenient. Using the same example:
type even_odd = Even int | Odd int f x = match x % 2 with 0 -> Even x | _ -> Odd x
In the above it would be convenient if any of the following would work:
-- let the inferencer figure out the return type: f: int -> _ -- let the inferencer figure out the parameter type: f: _ -> even_odd -- be explicit: f: int -> even_odd
Records
All of these will be useful and signatures with modules could form the basis of something like behaviours (compile modules to actual modules with a namespace prefix).
It would be good if modules could be inferenced, to that end 1ML’s approach is under consideration but I don’t have a firm enough grasp of it yet. Motivating example from 1ML – Core and Modules United (F-ing First-class Modules):
module Table = if size > threshold then HashMap else TreeMap
Current reading list to figure it all out:
- A Type-Theoretic Approach to Higher-Order Modules with Sharing
- A Type System for Higher-Order Modules (Expanded Version)
- Type Systems for Modules Notes for Meeting #2 - this is helping me grasp some of the underlying themes
I’m not yet sure how 1ML will play with row polymorphism.
Similar to `go fmt`, a reformatting utility that operates on the AST directly in order to correctly format source files. This likely requires:
- comments as legitimate AST nodes
- overhaul/regularization of existing AST nodes to allow comments to be attached to them (preserves expression-orientedness)