<br><div class="gmail_quote">On Thu, Jun 27, 2013 at 2:14 AM, AntC <span dir="ltr">&lt;<a href="mailto:anthony_clayden@clear.net.nz" target="_blank">anthony_clayden@clear.net.nz</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="im">&gt; Edward Kmett &lt;ekmett &lt;at&gt; <a href="http://gmail.com" target="_blank">gmail.com</a>&gt; writes:<br>
&gt;<br>
&gt; Let me take a couple of minutes to summarize how the lens approach<br>
tackles the composition problem today without requiring confusing changes<br>
in the lexical structure of the language. <br>
<br>
</div>Thank you Edward, I do find the lens approach absolutely formidable. And I<br>
have tried to read the (plentiful) documentation. But I haven&#39;t seen a<br>
really, really simple example that shows the correspondence with H98<br>
records and fields -- as simple as Adam&#39;s example in the wiki. (And this<br>
message from you doesn&#39;t achieve that either. Sorry, but tl;dr, and there<br>
isn&#39;t even a record decl in it.)</blockquote><div><br></div><div>There was this one buried down near the bottom.</div><div><br></div><div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:13.200000762939453px;background-color:rgb(255,255,255)">
<font face="courier new, monospace">data Foo = Foo { _fooX, _fooY :: Int }</font></div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:13.200000762939453px;background-color:rgb(255,255,255)"><font face="courier new, monospace"><br>
</font></div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:13.200000762939453px;background-color:rgb(255,255,255)"><font face="courier new, monospace">fooY f (Foo x y) = Foo x &lt;$&gt; f y</font></div>
</div><div> </div><div>We could implement that lens more like:</div><div><br></div><div>fooY :: Lens&#39; Foo Int</div><div>fooY f s = (\a -&gt; r { _fooY = a }) &lt;$&gt; f (_fooY s) </div><div><br></div><div>if you really want to see more record sugar in there, but the code means the same thing.</div>
<div><br></div><div>So let me show you exactly what you just asked for. The correspondence with the getter and setter for the field:</div><div><br></div><div>The correspondence with the getter comes from choosing to use the appropriate functor. With some thought it becomes obvious that it should be Const. I won&#39;t explain why as that apparently triggers <b>tl;dr. </b>;)</div>
<div><br></div><div><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><font face="courier new, monospace">s ^. l = getConst (l Const s)</font></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)">
<font face="courier new, monospace"><br></font></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><font face="arial, helvetica, sans-serif">Recall that</font><font face="courier new, monospace"> fmap f (Const a) = Const a</font><font face="arial, helvetica, sans-serif">, so</font></pre>
<pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><font face="courier new, monospace"><br></font></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)">
<font face="courier new, monospace">s ^. fooY = getConst (</font><span style="font-family:arial;background-color:transparent">(\a -&gt; r { _fooY = a }) &lt;$&gt; Const (_fooY s)) = getConst (Const (_fooY s)) = _fooY s</span></pre>
<pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><span style="font-family:arial;background-color:transparent"><br></span></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)">
<span style="background-color:transparent;font-family:arial">and we can recover the setter by choosing the Functor to be Identity.</span></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)">
<span style="background-color:transparent;font-family:arial"><br></span></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><span style="background-color:transparent;font-family:arial">modify l f s = runIdentity (l (Identity . f) s)</span></pre>
<pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><span style="background-color:transparent;font-family:arial">modify fooY f s = runIdentity (fooY (Identity . f) s) = runIdentity (</span><span style="font-family:arial;background-color:transparent">(\a -&gt; r { _fooY = a }) &lt;$&gt; (Identity . f) (_fooY s) )</span></pre>
<pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><span style="font-family:arial;background-color:transparent"><br></span></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)">
<span style="font-family:arial;background-color:transparent">if you remove the newtype noise thats the same as</span></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><span style="font-family:arial;background-color:transparent"><br>
</span></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><span style="font-family:arial;background-color:transparent">modify fooY f s = s { _fooY = f (_fooY s) }</span></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)">
<span style="font-family:arial;background-color:transparent"><br></span></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><span style="font-family:arial;background-color:transparent">Similarly after expansion:</span></pre>
<pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><span style="font-family:arial;background-color:transparent"><br></span></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)">
<span style="font-family:arial;background-color:transparent">set fooY a s = s { _fooY = a }</span></pre><pre style="white-space:pre-wrap;color:rgb(34,34,34);background-color:rgb(255,255,255)"><br></pre></div><div><span style="font-family:arial;background-color:transparent">I sought to give a feel for the derivation in the previous email rather than specific examples, but to work through that and the laws takes a fair bit of text. There isn&#39;t any getting around it.</span></div>
<div><br></div><div><br></div><div><br></div><div>With language support one could envision an option where record declarations cause the generation of lenses using whatever scheme one was going to use for the &#39;magic (.)&#39; in the first place. </div>
<div><br></div><div>The only difference is you get something that can already be used as both the getter and setter and which can be composed with other known constructions as well, isomorphisms, getters, setters, traversals, prisms, and indexed variants all fit this same mold and have a consistent theoretical framework.</div>
<div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Does the lens approach meet SPJ&#39;s criteria of:<br>
<div class="im"> * It is the smallest increment I can come up with that<br>
   meaningfully addresses the #1 pain point (the inability to<br>
   re-use the same field name in different records).<br></div></blockquote><div><br></div><div>The lens approach is <i>orthogonal</i> to the SORF/DORF design issue. It simply provides a way to make the field accessors compose together in a more coherent way, and helps alleviate the need to conconct confusing semantics around (.), by showing that the existing ones are enough. </div>
<div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">
 * It is backward-compatible.<br></div></blockquote><div><br></div><div>Lens already works today. So I&#39;d dare say that the thing that works today is compatible with what already works today, yes. ;) </div><div><br></div>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">
</div>[I note BTW that as the &quot;Plan&quot; currently stands, the &#39;.field&#39; postfix<br>
pseudo-operator doesn&#39;t rate too high on backward-compatible.]<br>
<br>
I do think that freeing up the name space by not auto-generating a record-<br>
type-bound field selector will help some of the naming work-rounds in the<br>
lens TH.<br></blockquote><div><br></div><div>I&#39;m going to risk going back into <b>tl;dr</b> territory in response to the comment about lens TH:</div><div><br></div><div>Currently lens is pretty much non-commital about which strategy to use for field naming / namespace management.</div>
<div><br></div><div>We do have three template-haskell combinators that provide lenses for record types in lens, but they are more or less just &#39;what we can do in the existing ecosystem&#39;.</div><div><br></div><div>I am _not_ advocating any of these, merely describing what we already can do today with no changes required to the language at all.</div>
<div><br></div><div>makeLenses - does the bare minimum to allow for type changing assignment</div><div>makeClassy - allows for easy &#39;nested record types&#39; </div><div>makeFields - allows for highly ad hoc per field-name reuse</div>
<div><br></div><div>Consider</div><div><br></div><div>data Foo a = Foo { _fooBar :: Int, _fooBaz :: a }</div><div><br></div><div>and we can see what is generated by each.</div><div><br></div><div><b>makeLenses &#39;&#39;Foo</b></div>
<div><br></div><div>generates the minimum possible lens support</div><div><br></div><div>fooBar :: Lens&#39; (Foo a) Int</div><div><div>fooBar f s = (\a -&gt; s { _fooBar = a }) &lt;$&gt; f (_fooBar a)</div></div><div><br>
</div><div>fooBaz :: Lens (Foo a) (Foo b) a b</div><div><div>fooBaz f s = (\a -&gt; s { _fooBaz = a }) &lt;$&gt; f (_fooBaz a)</div></div><div><br></div><div><b>makeClassy &#39;&#39;Foo</b> generates</div><div><br></div><div>
class HasFoo t a | t -&gt; a where</div><div>   foo :: Lens&#39; t (Foo a)</div><div>   fooBar :: Lens&#39; t Int</div><div>   fooBaz :: Lens&#39; t a</div><div>   -- with default definitions of fooBar and fooBaz in terms of the simpler definitions above precomposed with foo</div>
<div><br></div><div>It then provides</div><div><br></div><div>instance HasFoo (Foo a) a where</div><div>  foo = id</div><div><br></div><div>This form is particularly nice when you want to be able to build up composite states that have &#39;Foo&#39; as part of a larger state.</div>
<div><br></div><div>data MyState = MyState { _myStateFoo :: Foo Double, _myStateX :: (Int, Double) }</div><div>makeClassy &#39;&#39;MyState</div><div><br></div><div>instance HasFoo MyState Double where</div><div>  foo = myStateFoo</div>
<div><br></div><div><div>This lets us write some pretty sexy code using HasFoo constraints and MonadState.</div><div><br></div><div>blah :: (MonadState s m, HasFoo s a) =&gt; m a </div><div>blah = do </div><div>  fooBar += 1</div>
<div>  use fooBaz</div><div><br></div></div><div>and that code can run in State Foo or State MyState or other transformer towers that offer a state that subsumes them transparently.</div><div><br></div><div>This doesn&#39;t give the holy grail of having perfect field name reuse, but it does give a weaker notion of reuse in that you can access fields in part of a larger whole.</div>
<div><br></div><div><div>I said above that I don&#39;t wholly endorse any one of these options, but I do view &#39;makeClassy&#39; as having effectively removed all pressure for a better record system from the language for me personally. It doesn&#39;t permit some of the wilder ad hoc overloadings, but the constraints on it feel very &quot;Haskelly&quot;.</div>
</div><div><br></div><div>Finally,</div><div><br></div><div>To provide full field name reuse, we currently use </div><div><br></div><div><b>makeFields &#39;&#39;Foo</b>  which is perhaps a bit closer to one of the existing record proposals.</div>
<div>  </div><div>It takes the membernames and uses rules to split it apart into data type name and field part, and then makes instances of Has&lt;FieldName&gt; for each one.</div><div><br></div><div>There are issues with all 3 of these approaches. I personally prefer the middle existing option, because I get complete control over naming even if I have to be more explicit.</div>
<div><br></div><div>I wasn&#39;t purporting to solve this portion of the record debate, however. </div><div><br></div><div>I was claiming that lenses offered a superior option to giving back &#39;r { f :: t } =&gt; r -&gt; t</div>
<div><br></div><div>-Edward</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
&gt; ...<br>
<br>
You say:<br>
<div class="im">&gt;<br>
&gt;  template-haskell functions for lens try to tackle the SORF/DORF-like<br>
aspects. These are what Greg Weber was referring to in that earlier email.<br>
&gt;<br>
<br>
</div>errm I didn&#39;t see an email from Greg(?)</blockquote><div><br></div><div>Sorry, I was dragged into this thread by Simon forwarding me an email -- apparently it was in another chain. </div><div><br></div><div>-Edward</div>
</div>