What Haskell Records Need

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

On 2/08/2012, at 5:34 PM, Jonathan Geddes wrote:
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.
I was taught that this kind of thing violates the Law of Demeter and that an object should not be mutating the parts of an acquaintance's parts, but should ask the acquaintance to do so. I'd say that a.b.c.d = val is at the very least a sign that some encapsulation did not happen. Semantic editor combinators are ingenious, but they make me feel somewhat uneasy, in that they really are in some sense about the *syntax* (or maybe the concrete representation) of things rather than their *semantics* (or maybe I mean the abstract value).

Isn't this exactly the problem solved by all the lens packages?
Current popular ones are fclabels [0] and data-lens [1].
[0] http://hackage.haskell.org/package/fclabels
[1] http://hackage.haskell.org/package/data-lens
On Thu, Aug 2, 2012 at 7:34 AM, Jonathan Geddes
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-Semantic...
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
participants (3)
-
Erik Hesselink
-
Jonathan Geddes
-
Richard O'Keefe