1

I have the following:

import Control.Applicative
import Control.Monad
import Language.Haskell.TH


mkExp :: [Name] -> ExpQ
mkExp (name:[]) = [| ZipList $(varE name) |]
mkExp (name:names) = [| ZipList $(varE name) <*> $(mkExp names) |]

zipN :: Int -> ExpQ
zipN n = do
  names <- mapM newName $ replicate n "x"
  fn <- newName "f"
  let vps = map varP (fn:names)
  lamE vps $ [| $(varE fn) <$> $(mkExp names) |]

I'd like $(zipN 2) to generate:

\f x y -> f <$> ZipList x <*> ZipList y

so that it has the type (a -> b -> c) -> [a] -> [b] -> [c]. But poking around the output from -ddump-splices and filtering out the noise, I've found that $(zipN 2) instead generates:

\f x y -> f <$> ((ZipList x) <*> (ZipList y))

with the type (a -> b) -> [a1 -> a] -> [a1] -> ZipList b. Similarly, $(zipN 3) generates:

\f x1 x2 x3 -> (f <$> ((ZipList x1) <*> ((ZipList x2) <*> (ZipList x3)))

so it looks like each instance of $([|...|]) is being replaced with (...) rather than ..., which is surprising to me, since the docs seemed to say that pairs of $( ) and [| |] "cancelled out."

Why does Template Haskell generate this AST, and what can I do to get a function from it with the correct type?

Patrick Collins
  • 10,306
  • 5
  • 30
  • 69
  • The `...` [must be an expression](https://downloads.haskell.org/~ghc/7.0.2/docs/html/users_guide/template-haskell.html). Template haskell operates at the level of expressions, not tokens. The expression `e = x <*> y` means the same thing as `e = (<*>) x y`. Substituting `e` into `f <$> e` or `(<$>) f e` results in `f <$> (x <*> y)` or `(<$>) f ((<*>) x y)`. This is different than `f <$> x <*> y` or `(f <$> x) <*> y` or `((<*>) (<$>) f x) y)`. – Cirdec May 05 '15 at 08:41

1 Answers1

2

Both <$> and <*> are left associative. You are right associating them.

You can build the expression so that the operators are left-associated instead.

mkExp' :: ExpQ -> [Name] -> ExpQ
mkExp' acc [] = acc
mkExp' acc (name:names) = mkExp'' [| $(acc) <$> ZipList $(varE name) |] names
    where
        mkExp'' acc [] = acc
        mkExp'' acc (name:names) = mkExp'' [| $(acc) <*> ZipList $(varE name) |] names

zipN :: Int -> ExpQ
zipN n = do
  names <- mapM newName $ replicate n "x"
  fn <- newName "f"
  let vps = map varP (fn:names)
  lamE vps $ mkExp' (varE fn) names
Cirdec
  • 24,019
  • 2
  • 50
  • 100
  • Thank you. I guess it didn't occur to me that building the AST in a certain way implicitly forced a certain associativity on the operations. Another quick question, if you have the time --- why does adding `getZipList $` before `$(acc)` cause a type error? In (what I assume to be) the equivalent code `\f x y -> getZipList $ f <$> ZipList x <*> ZipList y`, the types work out fine --- what's changed here? – Patrick Collins May 05 '15 at 08:43
  • 1
    @PatrickCollins You're probably making `(getZipList (f <$> ZipList x)) <*> ZipList y`. Try `[| getZipList $(mkExp' (varE fn) names) |]`. You want to `getZipList` the overall result, not some sub-expression nested deep in the left side of things. – Cirdec May 05 '15 at 08:49