记录动态解析的字段名称的更新

时间:2018-11-29 11:33:16

标签: haskell functional-programming generic-programming

我有一条定义如下的记录:

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 ...

2 个答案:

答案 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一一更新,这些函数将解析器的字符串值与列表中字段的字符串名匹配。