The language in which we express our ideas has a strong influence on our thought processes. ~ Knuth



readme-lhs is an example of mixing markdown and haskell code in literate style, aiming for rapid development from initial idea to rendered description. The project:

The example/example.lhs is then a one-stop shop for links, ideas, app, tester and example holder.

compilation recipe

stack build --test --exec "$(stack path --local-install-root)/bin/readme-lhs-example" --exec "$(stack path --local-bin)/pandoc -f markdown+lhs -i other/ example/example.lhs other/ -t html -o index.html --filter pandoc-include --mathjax" --file-watch

The above recipe, taking advantage of stack composability, builds the project, runs the test, renders this file as html, and then watches for file changes. Pandoc and pandoc-include are assumed to be installed via stack, so you might have to:

stack install pandoc
stack install pandoc-include


The bare bones of this process is available as a stack template:

stack new project-name readme-lhs
cd project-name
stack build
$(stack path --local-install-root)/bin/readme-lhs-example

The other/readme-lhs.hsfiles can always be edited, renamed etc and dropped into a directory, and stack will find it.

lhs format, and mixing lhs and md, can get tiresome, so there is also the other/readme-hs.hsfiles template, which splits the example.lhs into example.hs and

ghc options

{-# OPTIONS_GHC -Wall #-}


-- doctest doesn't look at the cabal file, so you need pragmas here
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}


import Protolude
import Options.Generic


data Opts w = Opts
    { number :: w ::: Maybe Integer <?> "The number you want to product to"
    deriving (Generic)

instance ParseRecord (Opts Wrapped)

main :: IO ()
main = do
    o :: Opts Unwrapped <- unwrapRecord "an example app for readme-lhs"
    let n = fromMaybe 10 (number o)
    let answer = product [1..n]
    putStrLn (show answer <> " 👍" :: Text)
    writeFile "other/"
        ("$\\prod_{i=1}^{" <> show n <> "} i = " <>
         show answer <> "$")


\(\prod_{i=1}^{10} i = 3628800\)


doctest is a lightweight test framework that checks example code.

-- | doctests
-- >>> let n = 10
-- >>> product [1..n]
-- 3628800