Conway in Haskell

At this point it’s becoming increasingly clear that I can ask Cursor to read any of the implementations of my Conway and translate that into any other programming language that I want; this is what I’ve done last year with NASM, Scala, and Bash, and this year with Haskell.

Yes, I have read Miran Lipovača’s masterpiece but, like, 15 years ago, and to be honest I never quite got into using Haskell beyond small experiments. So what’s a (lazy) developer to do in 2026? You guessed it. Particularly after writing a whole issue of De Programmatica Ipsum dedicated to the subject of Functional Programming, including an article about Philip Wadler, one of the fathers of the language.

And once again, just like every time I use Cursor, I just let it do its thing after a good long prompt… and the magic operated in an uncanny way. The translation worked at first try, and I have nothing else to say at this point.

I do not know if you’re fluent in Haskell, but this is the code I got at the end.

#!/usr/bin/env runghc

import Conway.Coord (Coord (..))
import Conway.World (World, beacon, blinker, block, evolve, glider, newWorld, tub)
import Control.Concurrent (ThreadId, myThreadId, threadDelay)
import Control.Exception (AsyncException (UserInterrupt), catch, throwIO, throwTo)
import System.Exit (exitSuccess)
import System.Posix.Signals (Handler (Catch), installHandler, sigINT)
import System.Process (callCommand)

clearScreen :: IO ()
clearScreen = callCommand "clear"

handleSigInt :: ThreadId -> IO ()
handleSigInt mainThreadId = throwTo mainThreadId UserInterrupt

nextWorld :: Int -> World -> IO ()
nextWorld generation world = do
  clearScreen
  let currentGeneration = generation + 1
  putStr (show world)
  putStrLn ("Generation " ++ show currentGeneration)
  threadDelay 500000
  nextWorld currentGeneration (evolve world)

main :: IO ()
main = do
  mainThreadId <- myThreadId
  _ <- installHandler sigINT (Catch (handleSigInt mainThreadId)) Nothing
  let aliveCells =
        blinker (Coord 0 1)
          ++ beacon (Coord 10 10)
          ++ glider (Coord 4 5)
          ++ block (Coord 1 10)
          ++ block (Coord 18 3)
          ++ tub (Coord 6 1)
      world = newWorld 30 aliveCells
  nextWorld 0 world `catch` handleUserInterrupt

handleUserInterrupt :: AsyncException -> IO ()
handleUserInterrupt UserInterrupt = do
  clearScreen
  exitSuccess
handleUserInterrupt exception = throwIO exception

The final result shares quite a few similarities with F#; it’s easy to see that there’s a lot of inspiration from Haskell in F#, with the obvious exceptions of .NET-specific features like attributes and such.

open System
open System.Threading
open ConwayLib
open Coord
open World

[<EntryPoint>]
let main argv =
    Console.CancelKeyPress.Add (fun arg ->
        Console.Clear()
        Environment.Exit(0))

    let blinker = blinker { X = 0; Y = 1 }
    let beacon = beacon { X = 10; Y = 10 }
    let glider = glider { X = 4; Y = 5 }
    let block1 = block { X = 1; Y = 10 }
    let block2 = block { X = 18; Y = 3 }
    let tub = tub { X = 6; Y = 1 }
    let aliveCells = blinker @ beacon @ glider @ block1 @ block2 @ tub

    let rec iterate world generation =
        Console.Clear()
        printfn "%s" (worldDescription world)
        printfn "Generation %d" (generation)
        Thread.Sleep(500)
        iterate (evolve world) (generation + 1)

    iterate (createWorld 30 aliveCells) 1

    // return an integer exit code
    0

This similarity is no coincidence. Don Syme, designer and architect of the F# programming language at Microsoft, acknowledged this legacy in his paper “The early history of F#” submitted for HOPL IV in 2020.

The history of the F# programming language is an arc drawn from the 1970s to the present day. Somewhere, back in the early 1970s, an idea was born in the mind of Robin Milner and his colleagues Lockwood Morris and Malcolm Newey of a succinct, fully type-inferred functional programming language suitable for manipulating structured information [Gordon 2000]. Building on the tradition of LISP (and indeed using LISP as their implementation vehicle), this language became ML Meta Language and is the root of a tradition of strongly typed functional programming languages that includes Edinburgh ML, Miranda, Haskell, Standard ML, OCaml, Elm, ReasonML and PureScript. F# is part of this family.
(…)
Initially, in late 2000, in conjunction with Reuben Thomas, I attempted an implementation of Haskell for .NET, using a direct translation from the Core intermediate representation of the Glasgow Haskell Compiler (GHC) to the .NET bytecode. This experience was partly successful: small programs ran. However, the advice of Simon Peyton Jones led me to believe that Haskell.NET couldn’t be successful for several technical and cultural reasons: (…)