我有一条定义如下的记录:
data MyData = MyData
{ name :: String
, addr :: String
... a lot of other fields of String type
}
下一步,我要创建成对(String, fieldName)
的列表,如下所示:
fields =
[ ("NAME", name)
, ("ADDRESS", addr)
, ... and for other fields
]
最后,我需要一个函数,该函数可以获取类型为MyData
的空记录,并逐个字段动态填充它,如下所示:
initByStrings strs = foldl (\ d (x, y) -> d{y=(findIn x strs)}) emptyMyData fields
在Haskell中如果没有如下所示的长时间单调构造,是否可能发生这种情况?
...
lst = map (\ x -> findIn x strs) fields
f lst where
f (name:addr:...) = MyData name addr ...
答案 0 :(得分:4)
这是泛型的用例。
import GHC.Generics
data MyData = MyData
{ ...
} deriving (Generic) -- extension: DerivingGeneric
Generic
类型类具有关联的类型Rep
和方法to
(和from
)
to :: MyData -> Rep MyData p {- ignore the p -}
Rep MyData
展开为由M1
,(:*:)
和K1
构造的类型:
Rep MyData =
M1 D _ (
M1 C _ (
( M1 S _ (K1 _ String) )
:*:
( M1 S _ (K1 _ String) )
)
)
-- the things hidden by underscores carry metadata about MyData
-- (type name, constructor name, field names, whether fields are lazy, etc.).
因此,如果您可以编写适用于M1
,(:*:)
,K1
的许多组合的函数,则可以使用{ {1}}。
MyData
我们需要四个to
实例。两个class GFromMap r where
gFromMap :: Map String String -> Maybe (r p) -- always ignore the p
-- extension: FlexibleContexts
fromMap :: (Generic a, GFromMap (Rep a)) => Map String String -> Maybe a
fromMap m = to <$> gFromMap m
和GFromMap
新类型带有我们不关心的有关M1 D
的信息(类型名称,构造函数名称)。
M1 C
一个用于产品MyData
-- extension: FlexibleInstances
instance GFromMap r => GFromMap (M1 D d r) where
gFromMap m = M1 <$> gFromMap m
instance GFromMap r => GFromMap (M1 C c r) where
gFromMap m = M1 <$> gFromMap m
还有一个用于字段,这里我们需要使用(:*:)
类型类,从与-- extension: TypeOperators
instance (GFromMap r1, GFromMap r2) => GFromMap (r1 :*: r2) where
gFromMap m = (:*:) <$> gFromMap m <*> gFromMap m
新类型关联的元数据s
中获取字段名称。
M1 S
要点:https://gist.github.com/Lysxia/f27c078faec11487df2828cdfb81752a
答案 1 :(得分:2)
解决方案是通过以下方式创建的:
字段列表具有更新记录中相应字段的功能:
fields =
[ ("NAME", (\d x -> d{name=x}))
, ("ADDRESS", (\d x -> d{addr=x}))
, ... and for other fields
]
用于初始化MyData
记录的函数如下:
initByStrings strs = foldl (\ d (x, y) -> y d(findIn x strs)}) emptyMyData fields
因此可以使用一些外部函数将记录字段从foldl
一一更新,这些函数将解析器的字符串值与列表中字段的字符串名匹配。