使用TemplateHaskell中的列表

时间:2013-04-30 01:57:27

标签: haskell template-haskell

Here's the tutorial I'm working from

他有一个例子tupleReplicate,它返回一个带值的函数并复制它:

tupleReplicate :: Int -> Q Exp
tupleReplicate n = do id <- newName "x"
                      return $ LamE (VarP id)
                                    (TupE $ replicate n $ VarE id)

那么VarE id会返回一个表达式,然后可以与replicate一起使用吗?我的问题是,如果id是一个列表,这将如何工作?我想做点什么:

let vals = mkName "vals"
LamE (ListP vals) (map reverse $ ListE vals)

,但不起作用,因为ListE返回Exp,而不是[Exp]

更一般地说,我想在TemplateHaskell中编写一个获取列表并将函数应用于它的函数。


有一些示例代码here,我正在尝试编写像

这样的函数
makeModel (id_ : name_ : []) = Person (fromSql id_) (fromSql name_)

1 个答案:

答案 0 :(得分:2)

首先,让我们开启一些扩展程序:

{-# LANGUAGE FlexibleInstances, TemplateHaskell #-}

import Language.Haskell.TH

现在我假冒一些数据类型和类,以保持与真实世界的交互低:

data Person = Person Int String deriving Show

class SQL a where 
  fromSql :: String -> a

instance SQL Int where fromSql = read
instance SQL String where fromSql = id -- This is why I needed FlexibleInstances

好的,现在我们需要决定我们想要生成什么代码。紧紧抓住你的例子,我们可以将makeModel定义为lambda表达式(下面的翻译):

LamE [ListP [VarP id,VarP name]] (AppE (AppE (ConE Person) (AppE (VarE fromSql) (VarE id))) (AppE (VarE fromSql) (VarE name)))
\       [         id,     name ] -> (    (         Person     (        fromSql        id ))   (         fromSql        name ))
\       [         id,     name ] ->                Person $            fromSql        id   $            fromSql        name 

(我不会说流利的Exp,我在ghci做了runQ [| \[id,name] -> Person (fromSql id) (fromSql name) |]!)

我选择使用字符串会定义标识符idname,因为您可以从表中读取它,但您也可以生成名为{{1}的标识符等等。

field_1

makeMakeModel qFieldNames qMapFunction qConstructor = -- ["id","name"] 'fromSql 'Person LamE [ListP (map VarP qFieldNames)] -- \ [id,name] $ foldl AppE (ConE qConstructor) -- Person [AppE (VarE qMapFunction) (VarE name)|name <- qFieldNames] -- $ id $ name makeModel fieldNames mapFunction constructor = do names <- mapM newName fieldNames return $ makeMakeModel names mapFunction constructor 中的行动:

ghci -XTemplateHaskell

请注意我们使用*Main> runQ $ makeModel ["id","name"] 'fromSql 'Person LamE [ListP [VarP id_0,VarP name_1]] (AppE (AppE (ConE Main.Person) (AppE (VarE Main.fromSql) (VarE id_0))) (AppE (VarE Main.fromSql) (VarE name_1))) *Main> $(makeModel ["id","name"] 'fromSql 'Person) ["1234","James"] Person 1234 "James" 制作的标识符如何获得序列号以使其唯一,而我们在前面使用短划线传递的标识符newName'fromSql保留为他们的实际定义。


如果您不想使用lambda表达式,可以使用

'Person

作为起点 - runQ [d| makeModel [id,name] = Person (fromSql id) (fromSql name) |] 用于函数定义。