Literate Haskell with Markdown

Vehbi Sinan Tunalioglu - Jan 9 - - Dev Community

This is a short guide to writing Literate Haskell programs using Markdown.

The source code of this very Web page is a Markdown file with a frontmatter. At
the same time, the source code is a Literate Haskell program, i.e. you can
compile and run it.

Let's write a small program.

First, we define a Haskell module:

module Main where
Enter fullscreen mode Exit fullscreen mode

... and then, define a main function:

main :: IO ()
main = putStrLn "Hello World!"
Enter fullscreen mode Exit fullscreen mode

By now, we have implemented a valid Haskell program that is embedded in our
Markdown document (the source code). We will define two more functions to
demonstrate doctest.

-- | Adds 7 to the given 'Int'.
--
-- >>> add7 35
-- 42
add7 :: Int -> Int
add7 = (+) 7

-- | Divides 42 by the given 'Int'.
--
-- >>> div42 1
-- 42
-- >>> div42 2
-- 21
-- >>> div42 3
-- 14
-- >>> div42 6
-- 7
-- >>> div42 7
-- 6
-- >>> div42 0
-- 0
-- >>> div42 42
-- 1
div42 :: Int -> Int
div42 = div 42
Enter fullscreen mode Exit fullscreen mode

We need to install markdown-unlit, a custom unlit program to extract Haskell
code from Markdown files. Once installed, we can compile our program:

$ ghc -pgmLmarkdown-unlit Main.lhs
[1 of 1] Compiling Main             ( Main.lhs, Main.o )
Linking Main ...
Enter fullscreen mode Exit fullscreen mode

This will produce your executable (Main) along with Main.o and Main.hi
files. You can run your program:

$ ./Main
Hello World!
Enter fullscreen mode Exit fullscreen mode

We could have run the program directly using runhaskell, too:

$ runhaskell -pgmLmarkdown-unlit Main.lhs
Hello World!
Enter fullscreen mode Exit fullscreen mode

Also, we can produce the Haskell code of interest:

$ markdown-unlit -h label Main.lhs Main.hs
Enter fullscreen mode Exit fullscreen mode

We can study Main.hs or run doctest on it (do not forget to re-generate
Main.hs after changing the source code):

$ doctest Main.hs
label:51: failure in expression `div42 0'
expected: 0
 but got: *** Exception: divide by zero
          ^

Examples: 8  Tried: 7  Errors: 0  Failures: 1
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .