是否有可能在运行时构建数据?我的意思是“读”功能,但适用[(字段名称,值)]。假设我有
data Street = Street String
data City = City String
data ZipCode = ZipCode String
data Address = Address {
street :: Street,
city :: City,
zipCode :: ZipCode
}
我想要一个像:
这样的功能genericConstructor :: (DataConstructable a) => String -> [(String, a)] -> a
所以我可以这样使用它:
genericConstructor "Address" [("street", Street "Baker"),
("city", City "London"),
("zipCode", ZipCode "12345")] :: Address
我不想要任何样板代码,寻找与Reflection API for Java类似的东西。 目前正在查看Data.Data和Data.Typeable模块,但看不到我如何实现它。
所有这一切的目的是在某些数据格式和haskell数据结构之间创建绑定。
答案 0 :(得分:2)
这就是你所要求的东西。
import Data.Data
import Data.Dynamic
import Data.Maybe
data City = City String deriving (Data, Typeable, Show, Eq)
data Street = Street String deriving (Data, Typeable, Show, Eq)
data Addr = Addr {
city :: City
,street :: Street} deriving (Show, Eq, Data, Typeable)
class Foo a where
genericConstr :: [(String,Dynamic)] -> a
instance Foo Addr where
genericConstr = buildAddr
lf ls nm = fromMaybe (error $ nm ++ " not found") (lookup nm ls >>= fromDynamic)
buildAddr ls = Addr {city = lf ls "city", street = lf ls "street"}
加载它,并在ghci:
*Foo> genericConstr [("street", toDyn (Street "Baker")), ("city", toDyn (City "London"))] :: Addr
Addr {city = City "London", street = Street "Baker"}
但这似乎对我来说很重要。这很棘手,因为Haskell要求在编译时解析所有类型;在这种情况下,您尝试使用运行时信息创建类型(例如字符串“Address”)。这是可能的,但你会在每一步都与类型系统作斗争。我同意Jason的说法,使用解析器可能是一种更好的方法。
答案 1 :(得分:0)
我认为你的一个问题是用
genericConstructor :: (DataConstructable a) => String -> [(String, a)] -> a
所有'参数'必须具有相同的可构造类型。因此,你需要的东西是
genericConstructor :: (DataConstructable a) => String -> [forall b. DataConstructable b => (String, b)] -> a
我不能完全确定怎么做,我必须承认。
从数据格式字符串中解析所有内容会不容易吗?