2013-04-30 1 views
5

Here's the tutorial I'm working from.Работа со списком в TemplateHaskell

У него есть пример, 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_) 
+0

'vals' - это список, наоборот? –

+0

К сожалению, я должен был указать, 'vals' - это« Имя », а не список. –

+0

Почему? 'ListE' принимает' [Exp] '. –

ответ

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 как лямбда-выражения (перевод внизу):

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, я 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 

В действии в 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 и 'Person сохраняются как их фактические определения.


Если вы не хотите использовать лямбда-выражения, вы можете использовать

runQ [d| makeModel [id,name] = Person (fromSql id) (fromSql name) |] 

в качестве отправной точки - [d| ... |] для определения функций.

+2

См. Также [этот вопрос и ответ] (http://stackoverflow.com/questions/10653507/haskell-convert-list-to-data/10654675#10654675) для другого примера. –

+0

Спасибо, AndrewC! Это очень помогает. –