26 Aug 2018

Typelevel addition

Consider the following canonical natural number addition:

data Nat := Z | S Nat

plus : Nat -> Nat -> Nat
plus Z y := y
plus (S x) y := S (plus x y)

It can be also rewritten to recourse on y instead of x, or even to return (plus x (S y)). But can we make it more interesting?

Turns out we can if we look at it from “types as propositions” point of view. The canonical plus we’ve just written states “if we have two nats, we can have another one”, which isn’t very informative; indeed, taking either of nats, their product or even plain 0 would also suffice this signature. Perhaps, we need to make a stronger statement?

plus' : (x : Nat) -> (y : Nat) ->
  (z : Nat,
    (x = Z, z = y) |
    (x' : Nat, z' : Nat,
     x = S x',
     z = S z',
     z' = plus x' y))

Oh well. That was quite a long one. You may even wonder whether it’s actually a valid type. Lets take it apart.

plus' : (x : Nat) -> (y : Nat) ->
  (z : Nat,

First two lines are obvious: we’re just stating inputs and assigning variables to them. Then we return some z and scary proof about it.

    (x = Z, z = y) |

We may either return a proof that x is 0 and z is y..

    (x' : Nat, z' : Nat,

..or, that there exist x’ and z’ in Nat, such that..

     x = S x',
     z = S z',

and

     z' = plus x' y))

Now, for implementation. Manually written one would look somewhat like this:

plus' Z y := y, inl (refl Z, refl y)
plus' (S x') y :=
  let z' := (plus' x' y)
  in S z',
     inr (x', z',
       refl (S x'),
       refl (S z'),
       refl z')

Admittedly, this becomes more and more messy, especially without any syntax highlighting. But it can also be easily seen that our definition is pretty mechanical; and indeed, type of plus' already contains enough information to uniquely pick out addition operation on nats.

Which means that:

  • smart compiler should be able to generate function body for us

  • if there is an alternative (with better performance, possibly) implementation, smart compiler can opt to use it

Hopefully, using old good Peano arithmetic wasn’t too boring of an example. The central idea here is much more general though: if you know what you want to get, you can describe it in a type.

P.S. Provided with some trivial type inference, we can actually write plus as

plus'' :
  (x : Nat, y : Nat) ->
  ( z : Nat
  , ( x = Z
    , z = y
    )
  | ( S x' = x
    , z' = plus'' x' y
    , z = S z'
    )
  )

Which looks almost like a regular function definition with pattern matching. But this shouldn’t deceive you: all these equalities are types (propositions), not assignment operators.

Comments

You need to access this site via 0net to read & write comments; alternatively, refer to contacts page