Fun syntax

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Fun syntax

Vlad Dumitrescu-4
Hi,

I am a little confused about the syntax for functional objects when the
referred function is remote. The way to define a fun is then
    Fun = {lists, reverse}

I find this a little inconsequent. Why not
    Fun = fun lists:reverse/1 ?

What I find even strange is that it works to write something like
    {lists, reverse}([1,2,3]).

Wouldn't it be cleaner with an unified syntax for funs? The tuple notation
looks like remains from the old, while it working side by side with the
normal Module:Function notation feels almost like a bug... Is it?

best regards,
Vlad

_________________________________________________________________
Med MSN Foto kan du enkelt dela med dig av dina fotografier och best?lla
kopior: http://photos.msn.se



Reply | Threaded
Open this post in threaded view
|

Fun syntax

Matthias Lang-2
Vlad Dumitrescu writes:

 > I am a little confused about the syntax for functional objects when the
 > referred function is remote. The way to define a fun is then
 >     Fun = {lists, reverse}

 > I find this a little inconsequent. Why not
 >     Fun = fun lists:reverse/1 ?

Are you mixing up several weakly-related issues? The tuple syntax for
funs is deprecated and has (almost?) nothing to do with 'local' or
'remote'. You can use the same fun syntax to send funs between local
and remote processes. Example:

   -------------------a.erl--------
   -module(a).
   -export([make_fun/0]).
   
   make_fun() ->
           fun(X) -> lists:reverse(X) end.
   
   -------------------one node---------
   (a)3> F = a:make_fun().
   #Fun<a.0.102191654>
   (a)4> pid(230,53,0) ! F.
   #Fun<a.0.102191654>
   
   -------------------another node---------
   (b)1> self().
   <230.53.0>
   (b)2> F = receive X -> X end.
   #Fun<a.0.102191654>
   (b)3> F([1,2,3]).
   [3,2,1]

The only coupling between "remote" and "local" I can think of in
relation to funs is that the code for the fun must be available on the
node it is executed on, i.e. if 'b' doesn't have access to
'a.beam', you get

   ** exited: {undef,[{#Fun<a.0.102191654>,[[1,2,3]]},
                     {erl_eval,expr,3},
                     {erl_eval,exprs,4},
                     {shell,eval_loop,2}]} **

In the example, the problem CAN be avoided by sending a "tuple-fun",
but you're up the proverbial shit creek without a paddle if the fun()
you wanted to send was something more complex, for instance:

   fun(X) -> lists:duplicate(lists:reverse(X)) end

So:

  1. You shouldn't be using the tuple syntax

  2. It is possible to abuse the tuple syntax to work around some
     fundamental design decisions in Erlang, but the result is
     ugly and non-general, and mucking around with the syntax won't
     fix that.

BTW, take a look at what happens if you send a fun() you define in the
shell around. It sends the code for the fun in the message. ;-)

Matthias


Reply | Threaded
Open this post in threaded view
|

Fun syntax

Happi
In reply to this post by Vlad Dumitrescu-4

Vlad Dumitrescu wrote:

> Hi,
>
> I am a little confused about the syntax for functional objects when the
> referred function is remote. The way to define a fun is then
>     Fun = {lists, reverse}

No, it is not. Don't tell anyone that this works ;)
The way to do it is:
Fun = fun(L) -> lists:reverse(L) end.
 
> I find this a little inconsequent. Why not
>     Fun = fun lists:reverse/1 ?

I think the reason is that the entity lists:reverse/1 does not exist at compile time, the address of the function can only be found at load time or at the time of the call. If you create a fun like:
Fun = reverse/1, then Fun is just an alias for the local function reverse/1 which can be found during compilation.
One could argue that fun lists:reverse/1 could be syntactic sugar for  fun(L) -> lists:reverse(L) end, but this might lead one to belive that this fun only depend on the module lists but in fact it would also depend on the current module (there would be alocal fun calling lists:reverse/1).


> What I find even strange is that it works to write something like
>     {lists, reverse}([1,2,3]).
> Wouldn't it be cleaner with an unified syntax for funs? The tuple notation
> looks like remains from the old, while it working side by side with the
> normal Module:Function notation feels almost like a bug... Is it?

Yes, this is terrible and ugly in so many ways... and it is going to go away.
The reason this still exists is because of the interaction with code updates.
In some generic servers you want to store dynamic callback functions. If you send a closure (fun) to these servers and then update the module that contained that closure the server will (probably) crash when trying to call the callback. But if you send it a tuple it will do a dynamic apply and the callback will work regardles of code updates (provided of course that the new module still implements the callback function in the same way.)
e.g.
 s(MF) ->
  receive
   {call,Arg} -> MF(Arg), s(MF);
    _ -> s(MF)
end.
 
But this can be expressed just as well with apply:
s(MF={M,F}) ->
  receive
   {call,Arg} -> apply(M,F,[Arg]), s(MF);
    _ -> s(MF)
end.


/Erik
--------------------------------------
 Eric Conspiracy Secret Laboratories      
I'm Happi, you should be happy.
Praeterea censeo "0xCA" scribere Erlang posse.

 



Reply | Threaded
Open this post in threaded view
|

Fun syntax & language feature poll

Happi
In reply to this post by Matthias Lang-2

Matthias Lang wrote:
>  > I am a little confused about the syntax for functional objects when the
>  > referred function is remote. The way to define a fun is then
>  >     Fun = {lists, reverse}
...
> Are you mixing up several weakly-related issues? The tuple syntax for
> funs is deprecated and has (almost?) nothing to do with 'local' or
> 'remote'. You can use the same fun syntax to send funs between local
> and remote processes.

I think you misunderstood Vlad, I am sure he meant local or remote call
as in calls within the same module (local) or calls to another module (remote)
which has nothing to do with local or remote processes.
Unfortunately these two types of calls also distinguishes which version of
module is to be called.

Speaking of which, why not remove this feature of Erlang (that a remote call
calls the latest version of loaded code) and instead introduce a special update
call like:
 enter_latest foo(...) ?


Except in the shell who uses this feature?
If you want to write an application that really supports upgrades you have think this
through and design with it in mind from the beginning anyway.

Removing this feature would do wonders to the possibilities of optimization...  

Comments, flames, arguments are welcome.

/Erik
--------------------------------------
 N. Erik M. Stenman
 Eric Conspiracy Secret Laboratories      
--------------------------------------
I'm Happi, you should be happy.
Praeterea censeo "0xCA" scribere Erlang posse.




Reply | Threaded
Open this post in threaded view
|

Fun syntax & language feature poll

Lennart Ohman
Apart from being against all new things  :-)

In my opinion it should be better with functionality where you can keep to
a certain version of a module in the function call (making it possible to
extend to having more than two versions) and only program with that feature
where it is necessary.

/Lennart


> Speaking of which, why not remove this feature of Erlang (that a remote call
> calls the latest version of loaded code) and instead introduce a special update
> call like:
>  enter_latest foo(...) ?
>
> Except in the shell who uses this feature?
> If you want to write an application that really supports upgrades you have think this
> through and design with it in mind from the beginning anyway.
>
> Removing this feature would do wonders to the possibilities of optimization...
>
> Comments, flames, arguments are welcome.
>
> /Erik
> --------------------------------------
>  N. Erik M. Stenman
>  Eric Conspiracy Secret Laboratories
> --------------------------------------
> I'm Happi, you should be happy.
> Praeterea censeo "0xCA" scribere Erlang posse.


-------------------------------------------------------------
Lennart Ohman                   phone   : +46-8-587 623 27
Sjoland & Thyselius Telecom AB  cellular: +46-70-552 6735
Sehlstedtsgatan 6               fax     : +46-8-667 8230
SE-115 28 STOCKHOLM, SWEDEN     email   : lennart.ohman


Reply | Threaded
Open this post in threaded view
|

Fun syntax & language feature poll

Thomas Lindgren-3
In reply to this post by Happi

> Speaking of which, why not remove this feature of Erlang (that a remote
call
> calls the latest version of loaded code) and instead introduce a special
update
> call like:
>  enter_latest foo(...) ?
>
>
> Except in the shell who uses this feature?
> If you want to write an application that really supports upgrades you have
think this
> through and design with it in mind from the beginning anyway.
>
> Removing this feature would do wonders to the possibilities of
optimization...

I'm being a month late in replying, but this is a hobby horse of mine and
YES, permitting optimization across module boundaries helps quite a bit.
Here's why, for those who are interested but not experts.

Basically, a compiler does better the more code it has to work with, and it
seems to me that in the case of Erlang, you need to cross module boundaries
to get enough context. At least my own experience (e.g., EUC'01) shows this
very clearly.

Here is an example of what can be done. Type analysis is an optimization
that permits simplification of primitive operations. When applicable, it
yields excellent speedups for Erlang-style languages (Prolog, Lisp, ...).

For example, take element(N,X). If nothing is known, the VM must check:

- that X is a tuple of size K
- that N is an integer in the range 1 =< N =< K
- finally, select the appropriate word from the heap and return it.

The same kind of tests are done in most BIFs. Often, the code is wrapped in
a C function as well, yielding extra overhead for parameter passing. It
makes you gnash your teeth to see it.

Now assume the analyzer finds that N is a small integer and X is a tuple of
known arity.
A native-code optimizer can then remove the tests, reducing the element/2
call to a single load. This is good, but there's more: the important but
somewhat subtle issue that the compiler can ALSO be more aggressive in
inlining primitives (rather than doing C function calls) if it doesn't have
to lay out lots of code for the failure cases. Even if the failure code is
never executed, it requires more compile-time, reduces the effectiveness of
optimizations and messes up the I-cache.

I duly did an experiment with type analysis of Erlang several years ago
(1997?) and found that PER-MODULE analysis of real code, meaning OTP, is
largely useless for this purpose[*]. I furthermore believe this is the case
for most real code, since modules are used to structure programs into
reusable chunks as well as provide the unit for code loading. Thus,
cross-module analysis and optimization are "on the roadmap" for high
performance, at least in my book.

What to do? A compiler can enable cross-module optimization, say with the
transparent module aggregation method I described at EUC'01, AND/OR we can
provide some mechanism for programmers to group modules on the language
level. Perhaps Richard's hierarchical name spaces, or some suitably modified
version thereof, could be drafted for this purpose?

Depending on how such an extension is done, we may also have to fix records,
since record names often are reused and the scope of a record declaration is
somewhat ad hoc.

Best,
Thomas

[*] What does this mean? The analyzer kept a single version of each
function, and normally found that nearly all functions could be called with
'anything' for every function argument, i.e., no information. This is
equivalent to saying that you could just analyze each function locally,
which normally yields few opportunities for type-based optimization.
(Another alternative to cross-module analysis is for the analyzer to keep
multiple versions of functions; eminently possible, but there is no
consensus on how to do this well, AFAIK, nor do we know how well it works.)