[Haskell-cafe] What Haskell Records Need

Jonathan Geddes geddes.jonathan at gmail.com
Thu Aug 2 07:34:46 CEST 2012


Greetings,

tl;dr - What Haskell Records need are
semantic editor combinators for free.

I know this is yet another Record proposal
among many, but none of them out there
strike me as being exactly what I want in
Haskell.

Take the following types from a contrived
example.

>type Salary = Integer
>
>data Job = Job
>  { title  :: String
>  , salary :: Salary
>  }
>
>data Person = Person
>  { name :: String
>  , job  :: Job
>  }

Since I've used record syntax, I get
getter/accessor functions (title, salary,
name, job) for free. Now suppose I want to
create an aggregate getter function: return
the salary of a given person. Piece of cake,
it's just function composition

>getSalary :: Person -> Salary
>getSalary = salary . job

Done! Now suppose I want to write a
setter/mutator function for the same nested
field

>setSalaryMessy :: Salary -> Person -> Person
>setSalaryMessy newSalary person =
>  person {
>    job = (job person) {
>      salary = newSalary
>    }
>  }

Ouch! And that's not even very deeply nested.
Imagine 4 or 5 levels deep. It really makes
Haskell feel clunky next to `a.b.c.d = val`
that you see in other languages. Of course
immutability means that the semantics of
Haskell are quite different (we're creating
new values here, not updating old ones) but
it's still common to model change using these
kinds of updates.

What if along with the free getters that
the compiler generates when we use record
syntax, we also got semantic editor
combinator (SEC) functions[0] that could be
used as follows?

>setSalary newSalary = job' $ salary' (const newSalary)
>
>giveRaise amount = job' $ salary' (+amount)
>
>givePercentRaise percent = job' $ salary' (*(1+percent))

For each field x, the compiler generates a
function x' (the tic is mnemonic for change).
These little functions aren't hard to write,
but they're classic boilerplate.

>job' :: (Job -> Job) -> Person -> Person
>job' f person = person {job = f $ job person}

>salary' :: (Salary -> Salary) -> Job -> Job
>salary' f job = job { salary = f $ salary job}

These type of utility functions are a dream
when working with any reference type or
State Monad.

> modify $ givePercentRaise 0.25

The compiler could also generate polymorphic
SEC functions for polymorphic fields.
Further, the compiler could disallow using
old-style update syntax for fields whose SEC
update function is not in scope, giving us
fine-grained control over access and update.
On the other hand we currently have to create
new functions to achieve this (exporting the
getter means exporting the ability to update
as well, currently).

Of course this doesn't address the
namespacing issues with records, but it is
likely nicely orthogonal to other proposals
which do.

Also note that there's a package on hackage [1]
that will generate SEC functions using TH.
It's nice, but I prefer the style of field
names used above for updaters (field' vs
editField).

Let me know what you think. I'll write up an
official proposal if there's a bit of
general interest around this.

Thanks for reading,

--Jonathan

[0] - http://conal.net/blog/posts/semantic-editor-combinators
[1] -
http://hackage.haskell.org/packages/archive/sec/0.0.1/doc/html/Data-SemanticEditors.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20120801/312e4ca7/attachment.htm>


More information about the Haskell-Cafe mailing list