144

Let's say I have the following record ADT:

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

I want a function that takes a record and returns a record (of the same type) where all but one of the fields have identical values to the one passed as argument, like so:

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

The above works, but for a record with more fields (say 10), creating a such function would entail a lot of typing that I feel is quite unnecessary.

Are there any less tedious ways of doing the same?

Chris Stryczynski
  • 30,145
  • 48
  • 175
  • 286
  • 4
    Record syntax for updating exists, but quickly gets cumbersome. Take a look at [lenses](http://stackoverflow.com/questions/5767129/lenses-fclabels-data-accessor-which-library-for-structure-access-and-mutatio) instead. – Cat Plus Plus Feb 19 '13 at 13:15

3 Answers3

190

Yes, there's a nice way of updating record fields. In GHCi you can do --

> 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" }
Chris Taylor
  • 46,912
  • 15
  • 110
  • 154
  • 12
    The `RecordWildCards` extension can be nice as well, to “unpack” fields in a scope. For updates it’s not quite as nice though: `incrementA x@Foo{..} = x { a = succ a }` – Jon Purdy Feb 19 '13 at 14:14
  • 2
    BTW, in Frege (a Haskell for the JVM) you would define the function as ```updateFoo x = x.{ c = "Goodbye" }``` (note the ```.``` operator). – 0dB Feb 06 '16 at 14:34
  • Nice video by the way https://www.youtube.com/watch?v=YScIPA8RbVE – developer_hatch Nov 11 '19 at 23:23
  • Thanks. Sadly been a long time since I wrote any Haskell! – Chris Taylor Nov 12 '19 at 09:25
40

This is a good job for lenses:

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

test = Foo 1 2 "Hello"

Then:

setL c "Goodbye" test

would update field 'c' of 'test' to your string.

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
  • 5
    And lenses-like packages often define operators in addition to functions for getting and setting fields. For example, `test $ c .~ "Goodbye"` is how `lens` would do it iirc. I'm not saying this is intutitive, but once you know the operators then I expect it would come as easily as `$`. – Thomas M. DuBuisson Feb 19 '13 at 15:06
  • 3
    Do you know where _setL_ has gone? I'm importing _Control.Lens_, but ghc is reporting that _setL_ is undefined. – dbanas Aug 14 '17 at 13:12
  • 1
    use set instead of setL – Subhod I Mar 08 '19 at 08:12
  • That is awful procedual thinking. in Haskell, don’t say what it does, but what it *is*. That’s kinda the point of functional programming. Writing `test { c = "Goodbye" }` is a much more natural way of saying “`test`, but with `c` being `"Goodbye"`”. Whoever designed that `setL` function, missed the whole point of functional programming. … If record syntax has flaws, which it does, record syntax itself needs a fix. Not something that effectively turns Haskell into a C-like again. – Evi1M4chine Feb 23 '22 at 00:06
  • @Evi1M4chine did you have a look how lenses work under the hood? It looks imperative on the surface but it's absolutely wild category-theory-type-magic-trickery at it's finest. Check out: https://www.fpcomplete.com/haskell/tutorial/lens/ – Niklas Gruhn Jul 05 '22 at 20:20
  • @NiklasGruhn: Well, that makes it even worse. If I’m programming in a functional language, something should not be crammed into an imperative look in the first place. And while I like not dumbing down things, it should also not be incomprehensible magic trickery either. They key word is *emergence*. Lots of power *and* from very simple parts. Elegance and (coding) efficiency are related words. … What good is simplicity, if you can’t do anything anymore? (e.g. iPhones) And what good is power, if you can’t actually use it? (e.g. academic porn like this).… And I like category theory, so… :) – Evi1M4chine Jul 07 '22 at 18:23
  • @Evi1M4chine Totally agree. I just thought this might be the angle to convince you to give lenses a try. Anyway, lenses are not about crunching the pesky imperative idioms into Haskell just so the normi devs feel more comfortable. Lenses solve real problems and exert nice functional properties like composability. Just because it syntactically resembles imperative style doesn’t have to mean it’s a bad concept – Niklas Gruhn Jul 08 '22 at 12:46
27

You don’t need to define auxiliary functions or employ lenses. Standard Haskell has already what you need. Let’s take the example by Don Stewart:

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

test = Foo 1 2 "Hello"

Then you can just say test { c = "Goodbye" } to get an updated record.

Wolfgang Jeltsch
  • 781
  • 5
  • 10