
On 2 Aug 2012, at 09:25, Erik Hesselink wrote:
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
Not sure what all of these do, but I have a simple solution I use in my work:
Take the following types from a contrived example.
type Salary = Integer
data Job = Job { title :: String , salary :: Salary }
Rather than setTitle :: String -> Job -> Job we lift the first argument and define setfTitle :: (String -> String) -> Job -> Job setfTitle f jobrec = jobrec{ title = f $ title jobrec } then setTitle = setfTitle . const This is all just boilerplate, so we continue setfSalary :: (Salary -> Salary) -> Job -> Job setfSalary f jobrec = jobrec{ salary = f $ salary jobrec }
data Person = Person { name :: String , job :: Job }
setfName :: (String -> String) -> Person -> Person setfName f prec = prec{ name = f $ name prec } setfJob :: (Job -> Job) -> Person -> Person setfJob f prec = prec{ job = f $ job prec } Now we can use function composition to do two levels setfTitleInPerson :: (String -> String) -> Person -> Person setfTitleInPerson = setfJob . setfTitle setTitleInPerson :: String -> Person -> Person setTitleInPerson = setfTitleInPerson . const Simple function composition works to put these together... I was frustrated by this problem a while back, and decided to approach it formally (I write literate Haskell/LateX documents), and went to work, doing the math with the intention of writing a suitable combinator, until I discovered I didn't need one .... lifting from X -> R -> R to (X -> X) -> R -> R gave me all I needed...
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
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-------------------------------------------------------------------- Andrew Butterfield Tel: +353-1-896-2517 Fax: +353-1-677-2204 Lero@TCD, Head of Foundations & Methods Research Group Director of Teaching and Learning - Undergraduate, School of Computer Science and Statistics, Room G.39, O'Reilly Institute, Trinity College, University of Dublin http://www.scss.tcd.ie/Andrew.Butterfield/ -------------------------------------------------------------------- -------------------------------------------------------------------- Andrew Butterfield Tel: +353-1-896-2517 Fax: +353-1-677-2204 Lero@TCD, Head of Foundations & Methods Research Group Director of Teaching and Learning - Undergraduate, School of Computer Science and Statistics, Room G.39, O'Reilly Institute, Trinity College, University of Dublin http://www.scss.tcd.ie/Andrew.Butterfield/ --------------------------------------------------------------------