在复制其余字段时分配记录中单个字段的简便方法?


118

假设我有以下记录ADT:

data Foo = Bar { a :: Integer, b :: String, c :: String }

我想要一个接受记录并返回记录(相同类型)的函数,其中除一个字段外,所有字段的值都与作为参数传递的值相同,如下所示:

walkDuck x = Bar { a = a x, b = b x, c = lemonadeStand (a x) (b x) }

上面的方法有效,但是对于具有更多字段(例如10)的记录,创建这样的函数将需要进行很多输入,我觉得这是不必要的。

有没有那么简单的乏味方法?


3
存在用于更新的记录语法,但是很快变得麻烦。看一下镜头
Cat Plus Plus

Answers:


154

是的,有一种更新记录字段的好方法。在GHCi中,您可以-

> data Foo = Foo { a :: Int, b :: Int, c :: String }  -- define a Foo
> let foo = Foo { a = 1, b = 2, c = "Hello" }         -- create a Foo
> let updateFoo x = x { c = "Goodbye" }               -- function to update Foos
> updateFoo foo                                       -- update the Foo
Foo {a = 1, b = 2, c = "Goodbye" }

9
RecordWildCards扩展名也可以很好,以“解压缩”合并范围中的字段。对于更新,它不是那么好:incrementA x@Foo{..} = x { a = succ a }
Jon Purdy 2013年

2
顺便说一句,在Frege(JVM的Haskell)中,您将函数定义为updateFoo x = x.{ c = "Goodbye" }(请注意.运算符)。
0dB


谢谢。自从我写任何Haskell以来,已经很久了!
克里斯·泰勒

37

对于镜片来说,这是一项好工作:

data Foo = Foo { a :: Int, b :: Int , c :: String }

test = Foo 1 2 "Hello"

然后:

setL c "Goodbye" test

会将“ test”的字段“ c”更新为您的字符串。


5
像镜头一样的包装除了用于获取和设置字段的功能外,通常还定义了运算符。例如,iirc test $ c .~ "Goodbye"是怎么lens做的。我并不是说这是直白的,但是一旦您了解运算符,我希望它会像一样容易$
Thomas M. DuBuisson

3
您知道setL去哪里了吗?我正在导入Control.Lens,但是ghc报告setL未定义。
dbanas '17

1
使用set代替setL
Subhod I

16

您无需定义辅助功能或使用镜头。Standard Haskell已经满足您的需求。让我们以Don Stewart为例:

data Foo = Foo { a :: Int, b :: Int , c :: String }

test = Foo 1 2 "Hello"

然后,您可以说test { c = "Goodbye" }要获取更新的记录。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.