我正在使用Data.Data模块在运行时动态获取数据类型的某些数据。假设有一个类似data Place = Place {name :: Text, description :: Text} deriving (Data)
的数据类型:
toConstr (Place "Some place" "Bla")
检索其构造函数,这将给我Place
constrFields $ toConstr (Place "Some place" "Bla")
检索其标签字段,这样我就会["name", "description"]
现在我需要的是获取我构建Place的值,所以对于Place "Some place" "Bla"
我想提取类似["Some place", "Bla"]
的东西,我的代码中的抓点是我不喜欢我们不知道这个数据值是Place
,它可以是派生Data
类的任何数据类型。 Icn代码:
getValuesOfDataValue :: (Data a) => a -> [String]
getValuesOfDataValue a =
-- some magic generic function
data Place = Place {name :: Text, description :: Text} deriving (Data)
-- the code below should evaluate to ["Some place", "Bla"]
getValuesOfDataValue (Place "Some place" "Bla")
data SomeType = SomeType {num :: Integer, num2 :: Integer} deriving (Data)
-- the code below should evaluate to [300, 500]
getValuesOfDataValue (SomeType 300 500)
我该如何做到这一点?
注意:getValuesOfDataValue
不必返回完全[String]类型,只需要将值打包在某些内容中。
答案 0 :(得分:4)
使用cast
家庭中的Typeable
(请回想Data
是Typeable
的子类)。
λ> import Data.Text
λ> import Data.Data
λ> :set -XDeriveDataTypeable -XOverloadedStrings
λ> data Triple = Triple Text Text Int deriving (Show, Data)
λ> gmapQ cast (Triple "a" "b" 1821) :: [Maybe Text]
[Just "a",Just "b",Nothing]
λ> gmapQ cast (Triple "a" "b" 1821) :: [Maybe Int]
[Nothing,Nothing,Just 1821]
采用动态类型编程语言。
如果您事先不知道自己想要哪种类型,也可以使用gshow
包中的syb
字符串化值:
λ> :set -package syb
λ> import Data.Generics.Text
λ> data Triple = Triple Text Text Int deriving (Data)
λ> gmapQ gshow (Triple "a" "b" 1821)
["(pack \"a\")","(pack \"b\")","(1821)"]
我会警告你:不知道你想要哪种类型会严重限制你可以用泛型做什么。并非所有东西都可以被字符串化,即使它们很丑陋(如上所示)。即使知道你想要什么类型的白名单也会给你带来很大帮助:
λ> import Control.Arrow
λ> :set -XScopedTypeVariables
λ> let show' (proxy :: Proxy a) = Kleisli (\x -> show <$> (cast x :: Maybe a))
λ> gmapQ (runKleisli (show' (Proxy :: Proxy Int) <+> show' (Proxy :: Proxy Text))) (Triple "a" "b" 1821)
["a","b","1821"]
注意:getValuesOfDataValue不必返回完全[String]类型,只需要将值打包在某些内容中。
问题是要将其打包。gshow
的工作方式是递归调用gmapQ
(及其助手extQ
,它是从gmapQ
构建的cast
值上的Data a => a
}:
-- | Generic show: an alternative to \"deriving Show\"
gshow :: Data a => a -> String
gshow x = gshows x ""
-- | Generic shows
gshows :: Data a => a -> ShowS
-- This is a prefix-show using surrounding "(" and ")",
-- where we recurse into subterms with gmapQ.
gshows = ( \t ->
showChar '('
. (showString . showConstr . toConstr $ t)
. (foldr (.) id . gmapQ ((showChar ' ' .) . gshows) $ t)
. showChar ')'
) `extQ` (shows :: String -> ShowS)
它具有String -> ShowS
的基本情况,因此每当它命中一个字符串时它就知道返回它并终止。在不知道有关您的问题域的更多详细信息的情况下,我会告诉您使用相同的策略出去构建自己的gshows
。在您想要打包每个类型的一般情况下,没有答案,但可能存在特定任务的具体内容。