[erlang-questions] Updates, lenses, and why cross-module inlining would be nice

Richard A. O'Keefe ok@REDACTED
Fri Nov 27 04:42:51 CET 2015

On 27/11/2015, at 2:35 am, Tristan Sloughter <t@REDACTED> wrote:

> Jesper also created https://github.com/jlouis/erl-lenses a few years
> back for this.

Somehow I had managed to forget that.
My sincere apologies.
Since we've just had a thread about updates, again, one of
many since he wrote that, it's certainly time the answer
was better known.

For what it's worth, my lens.erl fixes a performance
problem that his lens.erl still has  And I was very pleased
with myself for thinking up the fix until I discovered that
it was thoroughly familiar in the Haskell community.

> https://github.com/jlouis/erl-lenses

defines a lens as a pair {Get,Put}.


defines as lens as a triple {Get,Put,Update}.

Now *semantically* you should be able to replace

    update(Lens, Data, Fun)


    put(Lens, Data, Fun(get(Lens, Data)).

and indeed my lens.erl provides a function to fill in an
Update given a Get and Put.
But they are *pragmatically* not the same.

Let's take the simple case of a list of Numbers,
and adding 1 to the Nth of them.

    Lens = index(N),
    put(Lens, List, 1 + Get(Lens, List))

traverses the first N elements of the list *twice*,

    Lens = index(N),
    update(Lens, List, fun (X) -> X + 1 end)

traverses the first N elements of the list *once*.
From an actual measurement,

   put+get, full price : 100%
   update,  full price :  50%
   put+get, inlined    :  65%
   update,  inlined    :  45%

The inlining was done by hand.
Doing it in a single pass also applies to tree-like
data.  (I'll make another post suggesting an addition
to gb_trees.erl.)

(A word of warning: this is an example where the cost of
doing the update dwarfs the cost of going through a lens.
Don't expect the ratios to be so close for record fields.)

The really nice thing is that the update functions
are even easier to compose than the put functions.
I should have mentioned another law:

   Put(X, Y) = Update(X, fun (_) -> Y end).

Pierre Fenoll pierrefenoll@REDACTED said

> That's something the JIT should be doing, right?

Answer: cross-module inlining *changes the semantics*
of Erlang.  So with the exception of a handful of core
system modules, I would hope that HyPE *doesn't* do that.

Cross-module inlining should be licensed by an explicit
declaration of some kind.  Back in the last century I
proposed an
  -import_static(Module, [...Function...]).
directive to tell the compiler to bind the source module
to the version of Module available at that time to the
compiler (and to check at load time that the same version
was used).

> What happens when inlined code throws an error?
> Which location information is included?

I wouldn't expect cross-module inlining to be any different
from the inlining that's done now.  The point of inlining
is to integrate the called function into the calling
function.  The same exceptions would be raised, but
appearing to come from the caller, not the callee.
Since the actual code error would (almost always) be in the
caller, that's the place we want to look anyway.

More information about the erlang-questions mailing list