动态检索数据类型的值
Dynamically retrieve values of a data type
我正在使用 Data.Data 模块在运行时动态获取某种数据类型的一些数据。假设一个数据类型为 data Place = Place {name :: Text, description :: Text} deriving (Data)
:
- 我可以用 toConstr
toConstr (Place "Some place" "Bla")
检索它的构造函数,这会给我 Place
- 我可以用 constrFields
constrFields $ toConstr (Place "Some place" "Bla")
检索它的标签字段,这会给我 ["name", "description"]
现在我需要的是获取构建 Place 所用的值,因此对于 Place "Some place" "Bla"
我想提取类似 ["Some place", "Bla"]
的内容,问题在于我的代码 [=32] =]我不知道这个数据值是Place
,它可以是派生Data
class的任何数据类型。 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
不必 return 完全是一个 [String] 类型,它只需要将值打包在某些东西中。
使用 Typeable
家族的 cast
(回想一下 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]
接受动态类型编程语言。
如果您事先不知道您想要哪种类型,您也可以使用 syb
包中的 gshow
对值进行字符串化:
λ> :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"]
Note: getValuesOfDataValue doesn't have to return exactly a [String] type, it just need to have the values packed in something.
问题是将它打包成什么。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
的基本情况,所以每当它遇到一个字符串时,它就知道 return 它并终止。在不知道有关您的问题域的更多详细信息的情况下,我会建议您使用相同的策略出去构建您自己的 gshows
。在您想要打包 每种 类型的非常普遍的情况下,没有答案,但也许存在针对您的特定任务的特定内容。
我正在使用 Data.Data 模块在运行时动态获取某种数据类型的一些数据。假设一个数据类型为 data Place = Place {name :: Text, description :: Text} deriving (Data)
:
- 我可以用 toConstr
toConstr (Place "Some place" "Bla")
检索它的构造函数,这会给我Place
- 我可以用 constrFields
constrFields $ toConstr (Place "Some place" "Bla")
检索它的标签字段,这会给我["name", "description"]
现在我需要的是获取构建 Place 所用的值,因此对于 Place "Some place" "Bla"
我想提取类似 ["Some place", "Bla"]
的内容,问题在于我的代码 [=32] =]我不知道这个数据值是Place
,它可以是派生Data
class的任何数据类型。 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
不必 return 完全是一个 [String] 类型,它只需要将值打包在某些东西中。
使用 Typeable
家族的 cast
(回想一下 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]
接受动态类型编程语言。
如果您事先不知道您想要哪种类型,您也可以使用 syb
包中的 gshow
对值进行字符串化:
λ> :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"]
Note: getValuesOfDataValue doesn't have to return exactly a [String] type, it just need to have the values packed in something.
问题是将它打包成什么。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
的基本情况,所以每当它遇到一个字符串时,它就知道 return 它并终止。在不知道有关您的问题域的更多详细信息的情况下,我会建议您使用相同的策略出去构建您自己的 gshows
。在您想要打包 每种 类型的非常普遍的情况下,没有答案,但也许存在针对您的特定任务的特定内容。