---
title: Amulet updates
date: August 11, 2018
maths: true
---

Jesus, it's been a while. Though my last post was almost 6 months ago
(give or take a few), I've been busy working on
[Amulet](https://github.com/tmpim/amulet), which continues to grow,
almost an eldritch abomination you try desperately, but fail, to kill.

Since my last post, Amulet has changed a ton, in noticeable and
not-so-noticeable ways. Here are the major changes to the compiler since
then.

Parser improvements
===================

No language is good to use if it's inconvenient. So, in an effort to
make writing code more convenient, we've removed the need for `;;` after
top-level declarations, and added a _bunch_ of indentation sensitivity,
thus making several keywords optional: `begin`{.ocaml} and `end`{.ocaml}
are implicit in the body of a `fun`{.ocaml}, `match`{.ocaml}, or
`let`{.ocaml}, which has made those keywords almost entirely obsolete.
The body of a `let`{.ocaml} also need not be preceded by `in`{.ocaml} if
meaning is clear from indentation.

To demonstrate, where you would have

```ocaml
let foo =
  let bar = fun x -> begin
    a;
    b;
    c
  end in begin
    bar ();
    bar 1;
  end ;;
```

One can now write

```ocaml
let foo =
  let bar = fun x ->
    a
    b
    c
  bar ()
  bar 1
```

Moreover, we've added shorthand syntax for building and destructuring
records: `{ x, y, z }`{.ocaml} is equivalent to `{ x = x, y = y, z = z
}`{.ocaml} in both pattern and expression position.


Changes to record typing
========================

Whereas `{ x with a = b }` would extend the record `x` to contain a new
field `a` (with value `b`), it's now _monomorphic update_ of the record
`x`. That is: `x` must _already_ contain a field called `a`, with the
same type as `b`.

This lets you write a function for updating a field in a record, such as
the one below, which would previously be impossible. Supporting
polymorphic update is not a priority, but it'd be nice to have. The way
PureScript, another language with row-polymorphic records, implements
polymorphic update does not fit in with our constraint based type
system. A new type of constraint would have to be introduced
specifically for this, which while not impossible, is certainly
annoying.

```ocaml
let foo : forall 'row. { 'row | x : int } -> { 'row | x : int } =
  fun r -> { r with x = r.x + 1 }
```

The impossibility of supporting polymorphic update with regular
subsumption constraints $a \le b$ stems from the fact that, when faced
with such a constraint, the compiler must produce a coercion function
that turns _any_ $a$ into a $b$ _given the types alone_. This is
possible for, say, field narrowing---just pick out the fields you want
out of a bigger record---but not for update, since the compiler has no
way of turning, for instance, an `int`{.ocaml} into a `string`{.ocaml}.

Stronger support for Rank-N Types
=================================

Changes to how we handle subsumption have made it possible to store
polymorphic values in not only tuples, but also general records. For
instance:

```ocaml
let foo = {
  apply : forall 'a 'b. ('a -> 'b) -> 'a -> 'b =
    fun x -> x
} (* : { apply : forall 'a 'b. ('a -> 'b) -> 'a -> 'b } *)
```

`foo`{.ocaml} is a record containing a single polymorphic application
function. It can be used like so:

```ocaml
let () =
  let { apply } = foo
  apply (+ 1) 1
  apply (fun x -> x) ()
```

Pattern-matching Let
====================

A feature I've desired for a while now, `let` expressions (and
declarations!) can have a pattern as their left-hand sides, as
demonstrated above. These can be used for any ol' type, including for
cases where pattern matching would be refutable. I haven't gotten around
to actually implementing this yet, but in the future, pattern matching
in `let`s will be restricted to (arbitrary) product types only.

```ocaml
type option 'a = Some of 'a | None
type foo = Foo of { x : int }

let _ =
  let Some x = ... (* forbidden *)
  let Foo { x } = ... (* allowed *)
```

Even more "in-the-future", if we ever get around to adding attributes
like OCaml's, the check for this could be bypassed by annotating the
declaration with (say) a `[@partial]`{.ocaml} attribute.

Unfortunately, since Amulet _is_ a strict language, these are a bit
limited: They can not be recursive in _any_ way, neither directly nor
indirectly.

```ocaml
(* type error *)
let (a, b) = (b, a)

(* similarly *)
let (a, b) = x
and x = (a, b)
```

Cycle detection and warnings
============================

A verification pass is run over the AST if type-checking succeeds, to
forbid illegal uses of recursion (strict language) and, as an additional
convenience, warn when local variables go unused.

For instance, this is [forbidden](/static/verify_error.png):

```ocaml
let f = (fun x -> x) g
and g = (fun x -> x) f
```

And this gives a [warning](/static/verify_warn.png):

```ocaml
let _ =
  let { a, b } = { a = 1, b = 2 }
  ()
```

Plans for this include termination (and/or productivity) (as a
warning) and exhaustiveness checks (as an error).

No more `main`
============

Since pattern-matching `let`{.ocaml}s are allowed at top-level, there's no more
need for `main`. Instead of

```ocaml
let main () =
  ...
```

Just match on `()`{.ocaml} at top-level:

```ocaml
let () =
  ...
```

This gets rid of the (not so) subtle unsoundness introduced by the code
generator having to figure out how to invoke `main`, and the type
checker not checking that `main` has type `unit -> 'a`{.ocaml}, and also
allows us to remove much of the silly special-casing around variables
called `main`.

```ocaml
let main x = x + 1
(* attempt to perform arithmetic on a nil value *)
```

Implicit Parameters
===================

A bit like Scala's, these allow marking a function's parameter as
implicit and having the type checker find out what argument you meant
automatically. Their design is based on a bit of reading other compiler
code, and also the paper on modular implicits for OCaml. However, we do
not have a ML-style module system at all (much to my dismay, but it's
being worked on), much less first class modules.

Implicit parameters allow ad-hoc overloading based on dictionary passing
(like type classes, but with less inference).

```ocaml
type show 'a = Show of 'a -> string
let show ?(Show f) = f

let implicit show_string =
  Show (fun x -> x)

let "foo" = show "foo"
```

Here, unification makes it known that `show` is looking for an implicit
argument of type `show string`{.ocaml}, and the only possibility is
`show_string`{.ocaml}, which is what gets used.

Implicit laziness
=================

There is a built-in type `lazy : type -> type`{.ocaml}, a function
`force : forall 'a. lazy 'a -> 'a` for turning a thunk back into a
value, and a keyword `lazy` that makes a thunk out of any expression.
`lazy 'a`{.ocaml} and `'a` are mutual subtypes of eachother, and the
compiler inserts thunks/`force`{.ocaml}s where appropriate to make code
type-check.

```ocaml
let x && y = if x then force y else false
let () =
  false && launch_the_missiles ()

(* no missiles were launched in the execution of this program *)
```

General refactoring
===================

- Literal patterns are allowed for all types, and they're tested of
using `(==)`.

- Amulet only has one type constructor in its AST for all its kinds of
functions: `forall 'a. 'a -> int`{.ocaml}, `int -> string`{.ocaml} and `show string =>
unit`{.ocaml} are all represented the same internally and disambiguated
by dependency/visibility flags.

- Polymorphic recursion is checked using "outline types", computed
before fully-fledged inference kicks in based solely on the shape of
values. This lets us handle the function below without an annotation on
its return type by computing that `{ count = 1 }` _must_ have type `{
count : int }` beforehand.

  Combined with the annotation on `x`, this gives us a "full" type
  signature, which lets us use checking for `size`, allowing polymorphic
  recursion to happen.

~~~{.ocaml}
  type nested 'a = Nested of nested ('a * 'a) * nested ('a * 'a) | One of 'a
  let size (x : nested 'a) =
    match x with
    | One _ -> { count = 1 }
    | Nested (a, _) -> { count = 2 * (size a).count }
~~~

- The newtype elimination pass was rewritten once and, unfortunately,
disabled, since it was broken with some touchy code.

- Operator declarations like `let 2 + 2 = 5 in 2 + 2` are admissible.

- Sanity of optimisations is checked at runtime by running a type
checker over the intermediate language programs after all optimisations

- Local opens are allowed, with two syntaxes: Either
`M.( x + y)`{.ocaml} (or `M.{ a, b }`{.ocaml}) or `let open M in x +
y`{.ocaml}.

- Amulet is inconsistent in some more ways, such as `type : type`
holding.

- There are no more kinds.

Conclusion
==========

This post was a bit short, and also a bit hurried. Some of the features
here deserve better explanations, but I felt like giving them an
_outline_ (haha) would be better than leaving the blag to rot (yet
again).

Watch out for a blog post regarding (at _least_) implicit parameters,
which will definitely involve the changes to subtyping involving
records/tuples.
