Haskell 将函数映射到代数数据类型字段的更简洁的方法?

Haskell 将函数映射到代数数据类型字段的更简洁的方法?,haskell,algebraic-data-types,Haskell,Algebraic Data Types,我有一个包含很多字段的数据类型: data ManyFields a b c d .. = MF { f1 :: a, f2 :: b, f3 :: c .. } 问题一 如何将函数映射到每个字段,同时避免为每个字段实现map函数。 例如,这看起来非常单调乏味: -- | Note I am explicitly constructing ManyField after mapping a function onto the field -- | This looks bad mapf1 ::

我有一个包含很多字段的数据类型:

data ManyFields a b c d .. = MF { f1 :: a, f2 :: b, f3 :: c .. }
问题一

如何将函数映射到每个字段,同时避免为每个字段实现
map
函数。 例如,这看起来非常单调乏味:

-- | Note I am explicitly constructing ManyField after mapping a function onto the field
-- | This looks bad
mapf1 :: (a -> a1) -> ManyFields a b c ..  -> ManyFields a1 b c ..
mapf1 g mf = MF (g . f1 $ mf) (f2 mf) ..

-- | Repeat for each field
mapf2 :: (b -> b1) -> ManyFields a b c .. -> ManyFields a b1 c ...
我认为某种高阶函数可以抽象出
构造函数的模式。(mapfunction f)
会减少样板文件,但有更好的解决方案吗

问题二

如果我想将许多字段压缩在一起,并将任意算术的函数映射到每个字段上,这听起来像是某种类型类的实例吗

用例:

(==) `mapFunction` mf1 `pairWiseZipField` mf2 

在我看来,它有点像一个应用程序,但我也不确定如何在这种类型上实现
fmap

用标准函数无法实现这一点。最好的方法是为每个字段使用映射函数。幸运的是,您可以使用库中的一点模板haskell自动生成这些。它看起来像这样:

data ManyFields a b c d = MF { _f1 :: a, _f2 :: b, _f3 :: c, _f4 :: d }

makeLenses ''ManyFields
> foo = MF 'a' "b" 3 False
> foo^.f1
'a'
这将为
多个字段的每个字段生成一个镜头。镜头是一个简单的构造,允许您访问和更改其中的值——这些更改甚至可以是多态的,就像map!请注意每个字段的前缀是如何加下划线的:镜头与减去下划线的字段具有相同的名称

您现在可以访问如下值:

data ManyFields a b c d = MF { _f1 :: a, _f2 :: b, _f3 :: c, _f4 :: d }

makeLenses ''ManyFields
> foo = MF 'a' "b" 3 False
> foo^.f1
'a'
您可以使用
set
操作符设置值。当与镜头一起使用时,它会创建一个设置器功能:

> :t set f1
set f1 :: a' -> ManyFields a b c d -> ManyFields a' b c d
要实际使用它,您可以执行以下操作:

> set f1 () foo
MF () "b" 3 False
因为您有一个getter和setter,所以编写map函数非常简单。幸运的是,我们甚至不必这样做:库提供了一个名为
over
的函数:

> :t over f1 
over f1 :: (a -> a') -> ManyFields a b c d -> ManyFields a' b c d
如果您更喜欢中缀运算符,
set
也可以被称为
~
,over可以被称为
%~
。(后者有一个助记符:
%
是mod,或“modify”:P。)这对于刚刚翻转的
&
操作符也很有用。因此,以下两个版本是相等的:

> over f1 ord foo
MF 97 "b" 3 False
> foo & f1 %~ ord
MF 97 "b" 3 False
我个人认为运营商有点过分。除非你要到处使用镜头,否则我会坚持使用
set
over


我不知道有什么好的解决方案可以像你描述的那样压缩函数。但是一定要浏览一下镜头库的其余部分——它相当大,你永远不知道你会找到什么

我认为这是对镜头的极好介绍,我认为这是解决这个问题的最好办法。