Skip to content

Instantly share code, notes, and snippets.

@niuk
Created July 25, 2011 23:02
Show Gist options
  • Save niuk/1105485 to your computer and use it in GitHub Desktop.
Save niuk/1105485 to your computer and use it in GitHub Desktop.
main : IO ()
open : IO Handle
write : Handle -> String -> IO ()
close : Handle -> IO ()
main = do
fd <- open "log.txt"
write fd "Hello World!"
close fd
# this desugars to:
main =
open "log.txt" >>= \fd ->
write fd "Hello World!" >>= () ->
close fd
# if we model the (IO t) monad as a function that takes an opaque RealWorld value
# and returns a pair (t, RealWorld), then the above would turn into:
main world =
(\(fd, world) ->
(\((), world) ->
(close fd world)
) (write fd "Hello World!" world)
) (open "log.txt" world)
# The key here is aggressive inlining. The bind operator (>>=), due to its
# simple operation, should expand within the body of its caller if it is
# saturated, that is, if both of its arguments are immediately given.
# RealWorld is actually just a dummy; it does not take up any space in memory,
# so we can pretend that we are only passing the "important" values around,
# and that the IO functions can be called "raw":
main =
(\fd ->
(\() ->
(close fd)
) (write fd "Hello World!")
) (open "log.txt")
# Note that the two anonymous functions here are applied immediately. They
# are not returned, passed as arguments, or even given names. Thus, the
# compiler knows that the above can be transformed into:
main =
let fd = open "log.txt" in
let () = write fd "Hello World!" in
close fd
# Inlining is, again, key to this transformation. In general, lambdas that
# are "used up" immediately are equivalent to a let-block. At this point,
# translation into C (or LLVM) should be obvious:
main() {
int fd = open("log.txt");
write(fd, "Hello World!");
close(fd);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment