<div>Greetings,</div><div><br></div><div>tl;dr - What Haskell Records need are</div><div>semantic editor combinators for free.</div><div><br></div><div>I know this is yet another Record proposal</div><div>among many, but none of them out there</div>
<div>strike me as being exactly what I want in</div><div>Haskell.</div><div><br></div><div>Take the following types from a contrived</div><div>example.</div><div><br></div><div>>type Salary = Integer</div><div>></div>
<div>>data Job = Job</div><div>> { title :: String</div><div>> , salary :: Salary</div><div>> }</div><div>></div><div>>data Person = Person</div><div>> { name :: String</div><div>> , job :: Job</div>
<div>> }</div><div><br></div><div>Since I've used record syntax, I get</div><div>getter/accessor functions (title, salary,</div><div>name, job) for free. Now suppose I want to</div><div>create an aggregate getter function: return</div>
<div>the salary of a given person. Piece of cake,</div><div>it's just function composition</div><div><br></div><div>>getSalary :: Person -> Salary</div><div>>getSalary = salary . job</div><div><br></div><div>
Done! Now suppose I want to write a</div><div>setter/mutator function for the same nested</div><div>field</div><div><br></div><div>>setSalaryMessy :: Salary -> Person -> Person</div><div>>setSalaryMessy newSalary person =</div>
<div>> person {</div><div>> job = (job person) {</div><div>> salary = newSalary</div><div>> }</div><div>> }</div><div><br></div><div>Ouch! And that's not even very deeply nested.</div><div>
Imagine 4 or 5 levels deep. It really makes</div><div>Haskell feel clunky next to `a.b.c.d = val`</div><div>that you see in other languages. Of course</div><div>immutability means that the semantics of</div><div>Haskell are quite different (we're creating</div>
<div>new values here, not updating old ones) but</div><div>it's still common to model change using these</div><div>kinds of updates.</div><div><br></div><div>What if along with the free getters that</div><div>the compiler generates when we use record</div>
<div>syntax, we also got semantic editor</div><div>combinator (SEC) functions[0] that could be</div><div>used as follows?</div><div><br></div><div>>setSalary newSalary = job' $ salary' (const newSalary)</div><div>
></div><div>>giveRaise amount = job' $ salary' (+amount)</div><div>></div><div>>givePercentRaise percent = job' $ salary' (*(1+percent))</div><div><br></div><div>For each field x, the compiler generates a</div>
<div>function x' (the tic is mnemonic for change).</div><div>These little functions aren't hard to write,</div><div>but they're classic boilerplate.</div><div><br></div><div>>job' :: (Job -> Job) -> Person -> Person</div>
<div>>job' f person = person {job = f $ job person}</div><div><br></div><div>>salary' :: (Salary -> Salary) -> Job -> Job</div><div>>salary' f job = job { salary = f $ salary job}</div><div><br>
</div><div>These type of utility functions are a dream</div><div>when working with any reference type or</div><div>State Monad.</div><div><br></div><div>> modify $ givePercentRaise 0.25</div><div><br></div><div>The compiler could also generate polymorphic</div>
<div>SEC functions for polymorphic fields.</div><div>Further, the compiler could disallow using</div><div>old-style update syntax for fields whose SEC</div><div>update function is not in scope, giving us</div><div>fine-grained control over access and update.</div>
<div>On the other hand we currently have to create</div><div>new functions to achieve this (exporting the</div><div>getter means exporting the ability to update</div><div>as well, currently).</div><div><br></div><div>Of course this doesn't address the</div>
<div>namespacing issues with records, but it is</div><div>likely nicely orthogonal to other proposals</div><div>which do.</div><div><br></div><div>Also note that there's a package on hackage [1]</div><div>that will generate SEC functions using TH.</div>
<div>It's nice, but I prefer the style of field</div><div>names used above for updaters (field' vs</div><div>editField).</div><div><br></div><div>Let me know what you think. I'll write up an</div><div>official proposal if there's a bit of</div>
<div>general interest around this.</div><div><br></div><div>Thanks for reading,</div><div><br></div><div>--Jonathan</div><div><br></div><div>[0] - <a href="http://conal.net/blog/posts/semantic-editor-combinators">http://conal.net/blog/posts/semantic-editor-combinators</a></div>
<div>[1] - <a href="http://hackage.haskell.org/packages/archive/sec/0.0.1/doc/html/Data-SemanticEditors.html">http://hackage.haskell.org/packages/archive/sec/0.0.1/doc/html/Data-SemanticEditors.html</a></div><div><br></div>
<div><br></div><div><br></div>