Как вы уточняете / проходите определение записи в 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 используя стандартную рекурсию / сопоставление с образцом.

Другие вопросы по тегам