Vla*_*ala 5 haskell template-haskell
他有一个例子,tupleReplicate
它返回一个带值的函数并复制它:
tupleReplicate :: Int -> Q Exp
tupleReplicate n = do id <- newName "x"
return $ LamE (VarP id)
(TupE $ replicate n $ VarE id)
Run Code Online (Sandbox Code Playgroud)
那么VarE id
返回一个表达式,然后可以使用replicate
?我的问题是,如果id
是列表,这将如何工作?我想做的事情如下:
let vals = mkName "vals"
LamE (ListP vals) (map reverse $ ListE vals)
Run Code Online (Sandbox Code Playgroud)
除非因为ListE
退货Exp
而不起作用[Exp]
.
更一般地说,我想在TemplateHaskell中编写一个获取列表并将函数应用于它的函数.
这里也有一些示例代码在这里,我试图写这样一个函数
makeModel (id_ : name_ : []) = Person (fromSql id_) (fromSql name_)
Run Code Online (Sandbox Code Playgroud)
首先,让我们打开一些扩展:
{-# LANGUAGE FlexibleInstances, TemplateHaskell #-}
import Language.Haskell.TH
Run Code Online (Sandbox Code Playgroud)
现在,我将伪造一些数据类型和类,以保持与现实世界的低交互:
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
Run Code Online (Sandbox Code Playgroud)
好的,现在我们需要决定要生成什么代码。密切关注您的示例,我们可以将 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
Run Code Online (Sandbox Code Playgroud)
(我说得不太流利Exp
,我是runQ [| \[id,name] -> Person (fromSql id) (fromSql name) |]
用 ghci 说的!)
我选择使用字符串来定义标识符id
和name
,因为您可以从表中读取它,但您也可以生成名为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
Run Code Online (Sandbox Code Playgroud)
行动中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"
Run Code Online (Sandbox Code Playgroud)
请注意,我们使用的标识符如何newName
获得序列号以使其唯一,而我们在前面传入的标识符则带有破折号,'fromSql
并'Person
保留为它们的实际定义。
如果您不想使用 lambda 表达式,可以使用
runQ [d| makeModel [id,name] = Person (fromSql id) (fromSql name) |]
Run Code Online (Sandbox Code Playgroud)
作为您的起点 -[d| ... |]
用于函数定义。