为什么模板 Haskell 会添加意想不到的括号?

Why does Template Haskell add unexpected parens?

我有以下内容:

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) |]

我想$(zipN 2)生成:

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

所以它的类型是 (a -> b -> c) -> [a] -> [b] -> [c]。但是仔细查看 -ddump-splices 的输出并过滤掉噪音,我发现 $(zipN 2) 会生成:

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

类型为 (a -> b) -> [a1 -> a] -> [a1] -> ZipList b。同样,$(zipN 3) 生成:

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

所以看起来 $([|...|]) 的每个实例都被替换为 (...) 而不是 ...,这让我感到惊讶,因为文档似乎说成对的 $( )[| |] "cancelled out."

为什么模板 Haskell 会生成这个 AST,我该怎么做才能从中获取正确类型的函数?

<$><*> 都是左结合的。你把他们联系起来是对的。

您可以构建表达式,使运算符改为左关联。

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