I wanted to see how core.typed would deal with annotating some of my production Clojure code. After hitting an issue with protocols, and spending all of 5 minutes unable to fix it, my gen Y attention span kicked in and I lowered my goals. Instead I wrote a plain Clojure solution to Conway's Game of Life and then annotated it with core.typed. Then got a friend to re-write it in Scala. Then re-wrote it myself in Haskell.

This should give you the 10,000 feet view:

If you want the details view the full image or the source on github.

The algorithm is mostly the same across solutions. It's not the best but it makes use of some intermediate representations that I thought would be interesting to type check. The core idea is that by merging overlapping neighbourhoods that have a some understanding of the world you eventually arrive at the truth. When there are key collisions the cells are merged and they become more and more accurate. For instance (cell false 1) and (cell true 0) merge to (cell true 1) and we know that the cell is alive and has at least one neighbour.

You can see that typed Clojure solution was the largest. But I don't think it's unreasonably large. I couldn't bring myself to do a Java version but I'd expect it to be several times larger. There are a couple of limitations to the local type inferencing that this example hit.

Firstly for loops must be replaced with the for> macro. This takes inline type annotations for the return type and each variable binding. There are a few more cases where you need to use special macros, but not many.

Secondly functions passed to higher order functions needed annotations. This meant I had to break my anonymous functions out into top level definitions. Ambrose has written about this situation but it's unlikely to be overcome in the near future.

Even with these limitations I think it's awesome that a dynamic language can have a type system added to it without needing any compiler extensions. If you do too then help support the development of core.typed.