Skip to content

Latest commit

 

History

History
95 lines (72 loc) · 3.03 KB

Exercise01.md

File metadata and controls

95 lines (72 loc) · 3.03 KB
module Exercise01 where

import ClassyPrelude
import Language.Haskell.TH

Exercise 01

Let's start with a simple Template Haskell example. Goals of this exercise:

  1. Write a simple Hello World splice using the Q monad
  2. Debug our splices in GHCi

Hello World

Consider the following code:

helloWorldE :: Q Exp
helloWorldE = [| putStrLn "hello world" |]

helloWorldD :: Q [Dec]
helloWorldD =
  [d| helloWorld :: IO ()
      helloWorld = putStrLn "i'm trapped in a splice!"
    |]

helloWorldD' :: Q [Dec]
helloWorldD' = do
  helloWorldName <- newName "helloWorld"
  Just ioName <- lookupTypeName "IO"
  Just putStrLnName <- lookupValueName "putStrLn"
  sequence $
    [ sigD helloWorldName (appT (conT ioName) (tupleT 0))
    , valD (varP helloWorldName) (normalB (appE (varE putStrLnName) (litE (stringL "i'm too far gone!!")))) []
    ]

The above functions do more or less the same thing, but with some interesting differences. helloWorldE is an Exp (expression), or code that can be evaluated direcly. helloWorldD and helloWorldD' are Decs (declarations) which define new top-level declarations within a module. helloWorldD and helloWorldD' are different in that the former is QuasiQuoted (using the d identifier) while the latter is written directly in Template Haskell.

Configuring GHCi

Let's load this into GHCi:

cat > .ghci <<EOF
:set prompt "λ → "
:set prompt-cont "λ ‖ "
:set -pgmL markdown-unlit
EOF

stack ghci
:l exercises/Exercise01.lhs

Exercises

Calling TH splices in GHCi

In general, TH splices are called with $(<splice>).

We can't invoke all TH splices in GHCi. We can run $(helloWorldE) but we can't run $(helloWorldD). In order to use the declarations provided by $(helloWorldD) we'd have to put it in a file and reload the module. In addition, the splice has to be declared in a separate file from the one in which it is invoked. Try it out for yourself, if you'd like.

Debugging expressions in GHCi

Whenever we want, we can debug our TH splices using a utility. Try it out by replacing <splice> with a function of your choice.

putStrLn $(stringE . pprint =<< <splice>)

Other Notes

  • There are multiple QuasiQuote identifiers - [| ... |] or [e| ... |] for expressions, [d| ... |] for declarations, [t| ... |] for types, and [p| ... |] for patterns.
  • Names are identifiers for both values and types. We can promote a type to a Name to names with two single quotes: ''MyType, and a value to a name with one single quote: 'MyDataConstructor or even 'myVariable.
  • You can mix QuasiQuotes and splices together: [| $(StringL "foo") :: String |] and $(sigE (StringL "foo") [t| String |]) are both valid.
  • Lowercase TH functions and uppercase TH constructors are related.
    • varP :: Name -> PatQ
    • type PatQ = Q Pat
    • data Pat = ... | VarP Name | ...
  • Constructors and functions always end with the first letter of their type.
    • varE :: Name -> ExpQ
    • varT :: Name -> TypeQ
    • varP :: Name -> PatQ