Как вы уточняете / проходите определение записи в Haskell?
Дана запись (не экземпляр записи, само определение записи), например:
data Request
= Expand { shortUrl :: [String]
, hash :: [String]
}
| Shorten { longUrl :: String
, domain :: String
}
| LinkEdit { link :: String
, title :: Maybe String
, note :: Maybe String
, private :: Maybe Bool
, user_ts :: Maybe Int
, archived :: Maybe Bool
, edit :: [String]
}
как генерировать код, используя информацию из определения записи, например:
mkReqUrl :: Request -> String
mkReqUrl (Expand shortUrl hash) = mru "expand" (zr "shortUrl" [shortUrl] ++ zr "hash" [hash])
mkReqUrl (Shorten longUrl domain) = mru "shorten" (zr "longUrl" [longUrl] ++ zr "domain" [domain])
...
Я полагаю, что некоторое использование TemplateHaskell и универсальной библиотеки (например, Uniplate) может помочь. (Я достиг пика реализации Lens, поскольку она имеет дело с определениями записей, но я теряюсь в коде.).
2 ответа
GHC.Generics (на самом деле документы для версии, которая будет поставляться с GHC 7.8, вероятно, лучше читать; см. Ниже), вероятно, способ сделать это сейчас. Сначала выведите Generic
экземпляр для вашего типа:
{-# LANGUAGE DeriveGeneric #-}
data Request = ...
deriving Generic
Вы можете использовать from
преобразовать ваше значение в его тип общего представления и применить selName
в соответствующую часть этой структуры (один из S1 Blah_blah Foo...
биты).
Я оставлю эту последнюю часть в качестве упражнения для вас; структура Rep
типы супер византийские. Хотя, похоже, это будет хорошо задокументировано в новом GHC здесь.
Как сделать это с помощью шаблона Haskell
(следующее было отредактировано вручную, чтобы было легче читать)
> ghci -XTemplateHaskell
GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help
...
Prelude> :m + Language.Haskell.TH
Prelude Language.Haskell.TH> :{
data Request
= Expand { shortUrl :: [String]
, hash :: [String]
}
| Shorten { longUrl :: String
, domain :: String
}
| LinkEdit { link :: String
, title :: Maybe String
, note :: Maybe String
, private :: Maybe Bool
, user_ts :: Maybe Int
, archived :: Maybe Bool
, edit :: [String]
}
Prelude Language.Haskell.TH| :}
Prelude Language.Haskell.TH> $(stringE . show =<< reify ''Request)
...
"TyConI (DataD [] :Interactive.Request []
[RecC :Interactive.Expand [(:Interactive.shortUrl,NotStrict,AppT ListT (ConT GHC.Base.String))
,(:Interactive.hash,NotStrict,AppT ListT (ConT GHC.Base.String))]
,RecC :Interactive.Shorten [(:Interactive.longUrl,NotStrict,ConT GHC.Base.String)
,(:Interactive.domain,NotStrict,ConT GHC.Base.String)]
,RecC :Interactive.LinkEdit [(:Interactive.link,NotStrict,ConT GHC.Base.String)
,(:Interactive.title,NotStrict,AppT (ConT Data.Maybe.Maybe) (ConT GHC.Base.String))
,(:Interactive.note,NotStrict,AppT (ConT Data.Maybe.Maybe) (ConT GHC.Base.String))
,(:Interactive.private,NotStrict,AppT (ConT Data.Maybe.Maybe) (ConT GHC.Types.Bool))
,(:Interactive.user_ts,NotStrict,AppT (ConT Data.Maybe.Maybe) (ConT GHC.Types.Int))
,(:Interactive.archived,NotStrict,AppT (ConT Data.Maybe.Maybe) (ConT GHC.Types.Bool))
,(:Interactive.edit,NotStrict,AppT ListT (ConT GHC.Base.String))]]
[])"
Затем можно пройтись по структуре, возвращенной из reify
используя стандартную рекурсию / сопоставление с образцом.