<div dir="ltr"><div>It has the same problems as Richard alludes to: in order to run fast, you need functional optimizations to remove the intermediate functions and make access fast.<br><br></div>I toyed with building a parse transform for producing stuff, but I'd much rather want a JIT.<br><br></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Nov 26, 2015 at 2:35 PM, Tristan Sloughter <span dir="ltr"><<a href="mailto:t@crashfast.com" target="_blank">t@crashfast.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Jesper also created <a href="https://github.com/jlouis/erl-lenses" rel="noreferrer" target="_blank">https://github.com/jlouis/erl-lenses</a> a few years<br>
back for this.<br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
  Tristan Sloughter<br>
  <a href="mailto:t@crashfast.com">t@crashfast.com</a><br>
</font></span><div class="HOEnZb"><div class="h5"><br>
On Wed, Nov 25, 2015, at 10:52 PM, Richard A. O'Keefe wrote:<br>
> Summary:<br>
>  (1) A promising way to solve the nested updates problem<br>
>      is to be more functional, not less.<br>
>  (2) There's an approach we could steal from Haskell.<br>
>  (3) There's free code to play with, worth every penny.<br>
><br>
> Consider a simplified version of Pascal:<br>
><br>
>   <variable> ::= <identifier> <selector>*<br>
>   <selector> ::= <dereference> | <field> | <index><br>
>   <dereference> ::= '^'<br>
>   <field> ::= '.' <identifier><br>
>   <index> ::= '[' <expression> ']'<br>
><br>
> If we think of a selector as a function<br>
>   f :: var T1 -> var T2<br>
> it's clear that <identifier> <selector> is function<br>
> application and <selector 1> <selector 2> is<br>
> function composition.<br>
><br>
> Yet in Pascal, and C, and languages like them,<br>
> while you can *apply* selectors and *compose* them,<br>
> they cannot be passed as parameters or even named.<br>
><br>
> If a functional language has something like selectors,<br>
> they should be first class values, just like other<br>
> more-or-less-function-like things.  But what kind of<br>
> value?<br>
><br>
> I think it was Reynolds, analysing Algol 60, who came<br>
> up with the idea that a pass-by-name parameter was<br>
> really a *pair* of functions:  one to fetch a value<br>
> and one to store it, and thereby freed us from the<br>
> limiting idea that there needed to be a 'var T' type.<br>
><br>
> The Haskell community have taken this idea and<br>
> developed it.  There this concept is called a "Lens".<br>
><br>
> <a href="http://www.cs.otago.ac.nz/staffpriv/ok/lens.erl" rel="noreferrer" target="_blank">http://www.cs.otago.ac.nz/staffpriv/ok/lens.erl</a><br>
><br>
> is a crude implementation of lenses in Erlang.<br>
> A lens is a selector from (values of) type A to<br>
> (values of) type B.  It's represented as a triple<br>
><br>
>     {Get, Put, Upd}<br>
>     Get :: (A) -> B<br>
>     Put :: (A, B) -> A<br>
>     Upd :: (A, (B) -> B) -> A<br>
><br>
> The Get function says how to extract a B value from an<br>
> A value.  The Put function says how to put a B value<br>
> into an existing A value, returning a new A value.<br>
> The Upd function says how to compute a new A value by<br>
> extracting a B, applying a function to it, and putting<br>
> it back.  We expect the following laws to hold:<br>
><br>
>     Get(Put(A, B)) = B<br>
>     Put(A, Get(A)) = A<br>
>     Upd(A, Fun) = Put(A, Fun(Get(A)))<br>
><br>
> That is, if you make a lens by hand, you should ensure<br>
> that this is true.<br>
><br>
> There are functions for using lenses so you don't have<br>
> to think about the representation.<br>
><br>
>     lens:get(Lens, A) -> B<br>
>     lens:put(Lens, A, B) -> A'<br>
>     lens:update(Lens, A, F) -> A'<br>
><br>
> There are also functions for making simple lenses.<br>
> For example, if you have a record 'rec' with a field<br>
> 'fld', lens:tuple(#rec.fld) will give you a lens<br>
> for that field.<br>
><br>
> Warning: the functions in the lens returned by<br>
> lens:tuple(#rec.fld)<br>
> do NOT check at run time that they are dealing with<br>
> a 'rec' record.  They can't, because #rec.fld is just<br>
> a number, and you can't get back from that number to<br>
> 'rec' or the arity of the record.<br>
><br>
> The important thing is that these functions compose.<br>
><br>
> For example, suppose you have<br>
>   -record(foo, {boo,coo,goo,zoo}).<br>
> and Foozle is a list of these, and you want to find<br>
> the first record with boo=42, and increment its zoo<br>
> field by 137.<br>
><br>
>     Find = lens:where(fun (#foo{boo=Boo}) -> Boo == 42 end)<br>
><br>
> is a lens for finding the record you want and<br>
><br>
>     Field = lens:tuple(#foo.zoo)<br>
><br>
> is a lens for getting at the zoo field of such a record.<br>
> lens.erl provides composition of 2 .. 6 lenses.  So<br>
><br>
>     Selector = lens:c(Find, Field)<br>
><br>
> is a selector that refers to a field of a record in a list,<br>
> and<br>
><br>
>     lens:update(Selector, Foozle, fun (Zoo) -> Zoo + 137 end)<br>
><br>
> does the whole job (in just one pass through the list, too,<br>
> which is why the Upd function is there).<br>
><br>
> More simply,<br>
>     lens:c(lens:tuple(#a.b), lens:tuple(#c.d),<br>
>            lens:tuple(#e.f), lens:tuple(#g.h))<br>
> is the equivalent of .b.d.f.h in Pascal, except that it is<br>
> a value you can pass around like any other.<br>
><br>
> You are not limited to fields that actually exist as such.<br>
> For example, you could do<br>
><br>
> gb_set(Element) -><br>
>     {   fun (Set) -> gb_sets:is_member(Element, Set) end<br>
>     ,   fun (Set, false) -> gb_sets:delete_any(Element, Set)<br>
>           ; (Set, true)  -> gb_sets:add(Element, Set)<br>
>         end<br>
>     ,   fun (Set, Fun) -><br>
>             Old = gb_sets:is_member(Element, Set),<br>
>             case Fun(Old)<br>
>               of Old   -> Set<br>
>                ; true  -> gb_sets:insert(Element, Set)<br>
>                ; false -> gb_sets:delete(Element, Set)<br>
>             end<br>
>         end<br>
>     }.<br>
><br>
> and make a gb-set act like a dictionary with Boolean elements.<br>
><br>
> There's even a combinator all/1 so that<br>
>     All = lens:c(lens:tuple(1)),<br>
>     Data = [{3,a},{1,b},{4,c},{6,d}]<br>
>     lens:get(All, Data)<br>
>  => [3,1,4,6]<br>
>     lens:update(All, Data, fun (N) -> N*10 end)<br>
>  => [{30,a},{10,b},{40,c},{60,d}]<br>
><br>
> So what about cross-module inlining?<br>
><br>
> By the time you've made and composed a couple of lenses,<br>
> you have a tangle of funs.  If the compiler (a) always<br>
> inlines (fun (...) -> ... end)(...) and (b) inlines the<br>
> functions from the lens module, then the tangle resolves<br>
> tidily into reasonably straightforward code.<br>
><br>
> But the cross-module inlining step is crucial.  Without<br>
> that, the compiler has nothing to work on.<br>
><br>
> At its crudest, consider<br>
><br>
>     lens:get(lens:tuple(#rec.fld), Rec)<br>
><br>
> *With* cross-module inlining and inlining of funs that<br>
> are applied to known arguments and dead code elimination,<br>
> you end up with<br>
><br>
>     element(#rec.fld, Rec)<br>
><br>
> *Without* cross-module inlining, three funs are built,<br>
> only one of which will ever be called.<br>
><br>
><br>
> _______________________________________________<br>
> erlang-questions mailing list<br>
> <a href="mailto:erlang-questions@erlang.org">erlang-questions@erlang.org</a><br>
> <a href="http://erlang.org/mailman/listinfo/erlang-questions" rel="noreferrer" target="_blank">http://erlang.org/mailman/listinfo/erlang-questions</a><br>
_______________________________________________<br>
erlang-questions mailing list<br>
<a href="mailto:erlang-questions@erlang.org">erlang-questions@erlang.org</a><br>
<a href="http://erlang.org/mailman/listinfo/erlang-questions" rel="noreferrer" target="_blank">http://erlang.org/mailman/listinfo/erlang-questions</a><br>
</div></div></blockquote></div><br><br clear="all"><br>-- <br><div class="gmail_signature">J.</div>
</div>