Вы можете адаптировать мое решение для аналогичной проблемы . Он использует универсальные шаблоны scrap-your-template (SYB). Этот ответ довольно подробно объясняет код, поэтому я не буду его здесь объяснять. Ниже приведен пример кода, который работает с вашим примером. Он поддерживает типы данных со смесью полей Maybe String
и String
; другие типы полей игнорируются getKey
.
Обратите внимание, что это не будет очень эффективным. Если это основная часть вашего приложения logi c, а не какой-то отладочный прием, вам следует подумать о переосмыслении ваших типов данных. Может быть, вам на самом деле нужен здесь не тип записи Haskell, а какая-то двунаправленная карта (например, пары Map k v
и Map v k
, доступ к которым осуществляется через набор функций, которые поддерживают их согласованность).
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
import Data.Tuple
import Data.Generics
data HaskellRecord = HaskellRecord {
_key1 :: Maybe String
, _key2 :: Maybe String
, _key3 :: Maybe String
} deriving (Data)
-- Get field names (empty list if not record constructor)
getnames :: Data object => object -> [String]
getnames = constrFields . toConstr
-- Get field values as `Maybe String`s
getfields :: Data object => object -> [Maybe String]
getfields = gmapQ toString
-- Generic function to convert one field.
toString :: (Data a) => a -> Maybe String
toString = mkQ Nothing -- make a query with default value Nothing
id -- handle: id :: Maybe String -> Maybe String
`extQ` Just -- extend to: Just :: String -> Maybe String
-- Get field name/value pairs from any `Data` object.
getpairs :: Data object => object -> [(String, Maybe String)]
getpairs = zip <$> getnames <*> getfields
getKey :: Data record => String -> record -> Maybe String
getKey v = lookup (Just v) . map swap . getpairs
main :: IO ()
main = do
print $ getKey "value2" $
HaskellRecord {
_key1 = Just "value1",
_key2 = Just "value2",
_key3 = Just "value3"
}