primes = filterPrime [2..] where
filterPrime (p:xs) =
p : filterPrime [x | x <- xs, x `mod` p /= 0]
take 100 primes
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.
primes = filterPrime [2..] where
filterPrime (p:xs) =
p : filterPrime [x | x <- xs, x `mod` p /= 0]
take 100 primes
Edit the code and run it again by clicking the play button, or by pressing Ctrl+Enter. Alternatively, press Alt+Enter to run the code and create a new text area.
The interpreter accepts expressions along with definitions. Like GHCi,
expressions of type IO _ are run; otherwise, if the type has a Show
instance, the value is printed.
The espy function prints the combinators produced by my compiler.
Some Scott encodings:
espy True espy $ Just 'x' espy foldr
The following is a big mess. We can the dictionary selector has yet to be reduced:
abc = ['a'..'z'] espy abc
Evaluating the first 12 elements of the list cleans up the expression a little, thanks to laziness:
take 12 abc espy abc
Full reduction leads to the normal form:
abc espy abc
Another example. The * means itself. Again, we have unevaluated dictionary
selectors.
fac n = if 1 < n then n * fac (n - 1) else 1 :: Int espy fac
Much better:
fac 1 espy fac
Without the Int annotation, the fac function is polymorphic and hence more
verbose because it can always handle any Ring instance.