是否有可能在运行时构造数据?我的意思是“读”功能,但适用[(字段名称,值)].让我说我有
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数据结构之间创建绑定.
最佳答案 这是你要求的东西.
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的说法,使用解析器可能是一种更好的方法.