记录动态解析字段名称的更新
Record update for dynamically-resolved field name
我有如下定义的记录:
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 ...
解决方案是这样创建的:
字段列表具有更新记录中相应字段的功能:
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
使用一些外部函数从列表中字段的字符串名称解析器字符串值一一更新。
这是泛型的一个用例。
import GHC.Generics
data MyData = MyData
{ ...
} deriving (Generic) -- extension: DerivingGeneric
Generic
类型 class 有一个关联类型 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
的多种组合的函数,则可以通过组合获得 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
还有一个用于字段,这里我们需要使用 Selector
类型 class 从与 M1 S
新类型关联的元数据 s
中获取字段名称。
-- extension: ScopedTypeVariables, TypeFamilies
-- the type equality (a ~ String) is for better error messages when
-- a record has a field not of type String
instance (a ~ String, Selector s) => GFromMap (M1 S s (K1 i a)) where
gFromMap m = M1 <$> K1 <$> Map.lookup fdName m
where fdName = toUpper <$> selName (undefined :: _t s _r _a) -- we can refer to s thanks to ScopedTypeVariables
完整要点:https://gist.github.com/Lysxia/f27c078faec11487df2828cdfb81752a
我有如下定义的记录:
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 ...
解决方案是这样创建的:
字段列表具有更新记录中相应字段的功能:
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
使用一些外部函数从列表中字段的字符串名称解析器字符串值一一更新。
这是泛型的一个用例。
import GHC.Generics
data MyData = MyData
{ ...
} deriving (Generic) -- extension: DerivingGeneric
Generic
类型 class 有一个关联类型 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
的多种组合的函数,则可以通过组合获得 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
还有一个用于字段,这里我们需要使用 Selector
类型 class 从与 M1 S
新类型关联的元数据 s
中获取字段名称。
-- extension: ScopedTypeVariables, TypeFamilies
-- the type equality (a ~ String) is for better error messages when
-- a record has a field not of type String
instance (a ~ String, Selector s) => GFromMap (M1 S s (K1 i a)) where
gFromMap m = M1 <$> K1 <$> Map.lookup fdName m
where fdName = toUpper <$> selName (undefined :: _t s _r _a) -- we can refer to s thanks to ScopedTypeVariables
完整要点:https://gist.github.com/Lysxia/f27c078faec11487df2828cdfb81752a