primes = filterPrime [2..] where filterPrime (p:xs) = p : filterPrime [x | x <- xs, x `mod` p /= 0]
I gave a talk at Zurihac 2023. Macgyver’s Haskell Compiler: Combinators, Duct Tape, and Two Minutes:
Bubble VM: a visualization of combinator reduction
These take a while to load, because each page loads my compiler and then compiles code embedded in the page. See the git repo mentioned during the talk for build instructions.
Eagle-eyed viewers may have noticed localhost:3000
in my browser. This is
because I served these files using my
hoc
tool.
I used the Firenvim browser extension to open Neovim within a slide and then access a terminal to build my compiler.
Many thanks to the organizers of the event. A special thanks to Farhad Mehta for inviting me and for suggesting the title of my talk.
The slide showing the Church/Scott-encoding of Booleans should have used
f g
instead of x y
, as the variables correspond to different cases and
not fields.
I forgot to demo a few things, which is just as well as I was running out of energy towards the end. Perhaps I can show them off in a future talk. Or you can just try them out here.
The following "ChatFP" tool is a slightly prettier version of the Haskell compiler embedded on one of my slides.
Paste the example featured on haskell.org:
primes = filterPrime [2..] where filterPrime (p:xs) = p : filterPrime [x | x <- xs, x `mod` p /= 0]
Click the play button at the bottom. Since this is a definition, the input is
printed, and a new textarea
element appears. In general, this widget
accepts any number of definitions at once, that is, a Haskell program.
Now try take 100 primes
. This time, an expression is detected. Like GHCi,
those of type IO _
are run; otherwise, if the type has a Show
instance,
the value is printed.
Along with this interpreter, I also wanted to show the combinators produced by my compiler and demonstrate lazy evaluation via the following lines:
espy True -- Explore Scott encodings. espy $ Just 'x' espy foldr abc = ['a'..'z'] espy abc -- Shows a big mess; you can see the dictionary selector. take 12 abc espy abc -- Less messy, can see 12 constants. abc espy abc -- Normal form.
Another example:
fac n = if 1 < n then n * fac (n - 1) else 1 :: Int espy fac -- The `*` means itself. Unevaluated dictionary selectors. fac 1 espy fac -- Much better!
Without the Int
annotation, the fac
function is polymorphic and hence more
verbose because it can always handle any Ring
instance.
There is also a rudimentary module system, but I’d rather not explain it until I’ve improved it.