Jeffrey Benjamin Brown
Jeffrey Benjamin Brown

Reputation: 3709

Haskell macro to create a Map from their names to some expressions?

I have some variables a and b. I want to create the map Data.Map.fromList [("a",a),("b",b)] rapidly, by typing something like magic [a,b]. I want to do this live in GHCI, not from within a module.

I spent some time learning Template Haskell for this, but I still can't even tell if it's possible. Is it?

Upvotes: 2

Views: 114

Answers (1)

Carl
Carl

Reputation: 27003

Ok, here's a very quick and dirty implementation of magic:

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH
import qualified Data.Map as M

magic :: [String] -> ExpQ
magic xs = [| M.fromList $(listE (map toPair xs)) |]
  where
    toPair nameStr = do
        Just name <- lookupValueName nameStr
        [| (nameStr, $(varE name)) |]

Here is using it in ghci:

$ ghci -XTemplateHaskell thmagic.hs
GHCi, version 8.2.2: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Main             ( thmagic.hs, interpreted )
Ok, one module loaded.
*Main> let x = 1 ; y = 2 ; z = "hello"
*Main> $(magic ["x", "y"])
fromList [("x",1),("y",2)]
*Main> $(magic ["x", "y", "z"])

<interactive>:3:3: error:
    • No instance for (Num [Char]) arising from a use of ‘x’
    • In the expression: x
      In the expression: ("x", x)
      In the first argument of ‘M.fromList’, namely
        ‘[("x", x), ("y", y), ("z", z)]’

Note that template haskell is enabled in ghci, and the $() splice syntax is used to tell it to actually splice the generated expression in. Also note the compile error in the case where not every list entry would have the same type.

This code is quick and dirty, but the happy path is correct. Error cases result in code that fails to splice, but not necessarily with the friendliest error messages. All in all... it's a starting point.

Edit - Version with minimal input keystrokes, as described in the comments below:

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH
import qualified Data.Map as M

magic :: String -> ExpQ
magic names = [| M.fromList $(listE (map toPair (words names))) |]
  where
    toPair nameStr = do
        Just name <- lookupValueName nameStr
        [| (nameStr, $(varE name)) |]

Upvotes: 5

Related Questions