[erlang-questions] Improve $handle_undefined_function

120 views
Skip to first unread message

Loïc Hoguin

unread,
Jan 21, 2013, 1:04:50 PM1/21/13
to erlang-questions
Hello,

Just recently the $handle_undefined_function feature was introduced. It
is a way to override error_handler mechanisms when a function isn't
found, and works per module for all processes, instead of just one.

Regardless of the intent behind this feature, the implementation that
was pushed to master is in my opinion, and apparently others, deeply
flawed. The main issue is that it pushes the responsability of
propagating the right errors directly to the user, and the user must use
extra care when writing this handler.

For example this handler would be incorrect:

$handle_undefined_function(monitor, [process, Pid]) ->
do_something();
$handle_undefined_function(demonitor, Pid) ->
do_something_else();
$handle_undefined_function(F, Args) ->
error_handler:raise_undef_exception(?MODULE, F, Args).

Can you see why?[1] If it's not obvious to you, then perhaps this
feature needs more work.

My proposal is to use an attribute to list the functions defined like
this, so that error_handler can make the distinction between undef and
badarg directly. The above function would then be written like this:

$handle_undefined_function(monitor, [process, Pid]) ->
do_something();
$handle_undefined_function(demonitor, Pid) ->
do_something_else().

And the issue would be gone.

Adding an attribute isn't hard, whether you generate code or not, and it
can be used by tools to figure out what's going on with the module,
providing better integration.

And why not just use -export and still allow -spec anyway? The function
is exported, has specs, but is just defined elsewhere. I'm sure there
can be a simple solution that fits a lot better the existing toolbase.

[1] The answer is: if the first argument to 'monitor' is anything other
than the atom 'process', then an undef error will be triggered instead
of 'badarg'.

--
Loïc Hoguin
Erlang Cowboy
Nine Nines
http://ninenines.eu
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions

Tony Rogvall

unread,
Jan 21, 2013, 5:15:23 PM1/21/13
to Loïc Hoguin, erlang-questions
On 21 jan 2013, at 19:04, Loïc Hoguin <es...@ninenines.eu> wrote:

Hello,

Just recently the $handle_undefined_function feature was introduced. It is a way to override error_handler mechanisms when a function isn't found, and works per module for all processes, instead of just one.

Regardless of the intent behind this feature, the implementation that was pushed to master is in my opinion, and apparently others, deeply flawed. The main issue is that it pushes the responsability of propagating the right errors directly to the user, and the user must use extra care when writing this handler.

This is strange. You think that every one should stay away from this feature, but then you start to play
around with it your self. Why? Are better suited to handle this abomination than the rest of us or what? 

For example this handler would be incorrect:

$handle_undefined_function(monitor, [process, Pid]) ->
   do_something();
$handle_undefined_function(demonitor, Pid) ->
   do_something_else();
$handle_undefined_function(F, Args) ->
   error_handler:raise_undef_exception(?MODULE, F, Args).

Can you see why?[1] If it's not obvious to you, then perhaps this feature needs more work.

What is the fix? ...

My proposal is to use an attribute to list the functions defined like this, so that error_handler can make the distinction between undef and badarg directly. The above function would then be written like this:

Wow, you really want to contribute to the development of Erlang.

$handle_undefined_function(monitor, [process, Pid]) ->
   do_something();
$handle_undefined_function(demonitor, Pid) ->
   do_something_else().

And the issue would be gone.

Adding an attribute isn't hard, whether you generate code or not, and it can be used by tools to figure out what's going on with the module, providing better integration.

And why not just use -export and still allow -spec anyway? The function is exported, has specs, but is just defined elsewhere. I'm sure there can be a simple solution that fits a lot better the existing toolbase.

Now we are getting creative and cooperative, I like that.

Here is a smiley for you 

:-)

/Tony

[1] The answer is: if the first argument to 'monitor' is anything other than the atom 'process', then an undef error will be triggered instead of 'badarg'.

--
Loïc Hoguin
Erlang Cowboy
Nine Nines
http://ninenines.eu
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions

"Installing applications can lead to corruption over time. Applications gradually write over each other's libraries, partial upgrades occur, user and system errors happen, and minute changes may be unnoticeable and difficult to fix"



Fred Hebert

unread,
Jan 21, 2013, 5:26:27 PM1/21/13
to Lo�c Hoguin, erlang-questions
As long as the call order is well-defined to always be the same
(process-defined handler -> module-defined handler), there should be no
big consistency problem.

There should be work done in order to make sure that a crash in the
`'$handle_undefined_function'` handler does *not* change the error type
from `undef` to whatever the crash was.

In my opinion, this will allow to keep a more declarative type of error
handling there while keeping the caller's expectations sane, and will
put the burden of shoddy programming maintenance on the authors of the
original handler, and not push it to the user of the library.

Let's imagine I use a module that used this feature, gets updated, and
has a bug that breaks the functionality within
`'$handle_undefined_function'`. As the user of that module, I may get
anything from `undef` to whatever the handler uses and can generate
(`badarg`, `badarith`, `badfun`, etc.). This can be *very* confusing to
debug and manage.

On the other hand, if the error is restricted to `undef`, the unknown
behaviour is pushed back on the maintainer of the library to figure out,
and inspecting the module will show that the function is indeed missing.
It breaks fewer expectations.

I would also be in favor of a new error type of the kind
`module_author_uses_features_he_or_she_is_not_understanding_please_blame_them`
to be returned to make the problem 100% obvious and embarrassing. It
could be more insulting too. I'm barely kidding here.

I'm not against the idea, but I do believe more work is needed to make
sure users of libraries are protected from bad, shoddy implementations
of the function, especially in the age-old semantics of what fails or
not.

-Fred.

Loïc Hoguin

unread,
Jan 21, 2013, 6:10:13 PM1/21/13
to Tony Rogvall, erlang-questions
On 01/21/2013 11:15 PM, Tony Rogvall wrote:
>
> On 21 jan 2013, at 19:04, Loïc Hoguin <es...@ninenines.eu
> <mailto:es...@ninenines.eu>> wrote:
>
>> Hello,
>>
>> Just recently the $handle_undefined_function feature was introduced.
>> It is a way to override error_handler mechanisms when a function isn't
>> found, and works per module for all processes, instead of just one.
>>
>> Regardless of the intent behind this feature, the implementation that
>> was pushed to master is in my opinion, and apparently others, deeply
>> flawed. The main issue is that it pushes the responsability of
>> propagating the right errors directly to the user, and the user must
>> use extra care when writing this handler.
>>
> This is strange. You think that every one should stay away from this
> feature, but then you start to play
> around with it your self. Why? Are better suited to handle this
> abomination than the rest of us or what?

I said stay away because the implementation is flawed. If this feature
behaves the same as any other function call, then I'm perfectly good
with it.

Evan Miller

unread,
Jan 21, 2013, 6:15:00 PM1/21/13
to Loïc Hoguin, erlang-questions
On Mon, Jan 21, 2013 at 12:04 PM, Loïc Hoguin <es...@ninenines.eu> wrote:
Hello,

Just recently the $handle_undefined_function feature was introduced. It is a way to override error_handler mechanisms when a function isn't found, and works per module for all processes, instead of just one.

Regardless of the intent behind this feature, the implementation that was pushed to master is in my opinion, and apparently others, deeply flawed. The main issue is that it pushes the responsability of propagating the right errors directly to the user, and the user must use extra care when writing this handler.

For example this handler would be incorrect:

$handle_undefined_function(monitor, [process, Pid]) ->
    do_something();
$handle_undefined_function(demonitor, Pid) ->
    do_something_else();
$handle_undefined_function(F, Args) ->
    error_handler:raise_undef_exception(?MODULE, F, Args).

Can you see why?[1] If it's not obvious to you, then perhaps this feature needs more work.

My proposal is to use an attribute to list the functions defined like this, so that error_handler can make the distinction between undef and badarg directly. The above function would then be written like this:

$handle_undefined_function(monitor, [process, Pid]) ->
    do_something();
$handle_undefined_function(demonitor, Pid) ->
    do_something_else().

And the issue would be gone.

This is a great point. An attribute is one solution. Another would be adding another function, e.g. $should_handle_undefined_function(FunctionName, Arity), that determines at run-time whether to call the handler.

$should_handle_undefined_function(monitor, 2) -> true;
$should_handle_undefined_function(demonitor, 1) -> true;
$should_handle_undefined_function(_, _) -> false.

$handle_undefined_function(monitor, [process, Pid]) -> do_something();
$handle_undefined_function(demonitor, Pid) -> do_something_else().

What do you think?

Evan

Loïc Hoguin

unread,
Jan 21, 2013, 6:24:12 PM1/21/13
to Evan Miller, erlang-questions
On 01/22/2013 12:15 AM, Evan Miller wrote:
> On Mon, Jan 21, 2013 at 12:04 PM, Loïc Hoguin <es...@ninenines.eu
> <mailto:es...@ninenines.eu>> wrote:
>
> Hello,
>
> Just recently the $handle_undefined_function feature was introduced.
> It is a way to override error_handler mechanisms when a function
> isn't found, and works per module for all processes, instead of just
> one.
>
> Regardless of the intent behind this feature, the implementation
> that was pushed to master is in my opinion, and apparently others,
> deeply flawed. The main issue is that it pushes the responsability
> of propagating the right errors directly to the user, and the user
> must use extra care when writing this handler.
>
> For example this handler would be incorrect:
>
> $handle_undefined_function(__monitor, [process, Pid]) ->
> do_something();
> $handle_undefined_function(__demonitor, Pid) ->
> do_something_else();
> $handle_undefined_function(F, Args) ->
> error_handler:raise_undef___exception(?MODULE, F, Args).
>
> Can you see why?[1] If it's not obvious to you, then perhaps this
> feature needs more work.
>
> My proposal is to use an attribute to list the functions defined
> like this, so that error_handler can make the distinction between
> undef and badarg directly. The above function would then be written
> like this:
>
> $handle_undefined_function(__monitor, [process, Pid]) ->
> do_something();
> $handle_undefined_function(__demonitor, Pid) ->
> do_something_else().
>
> And the issue would be gone.
>
>
> This is a great point. An attribute is one solution. Another would be
> adding another function, e.g.
> $should_handle_undefined_function(FunctionName, Arity), that determines
> at run-time whether to call the handler.
>
> $should_handle_undefined_function(monitor, 2) -> true;
> $should_handle_undefined_function(demonitor, 1) -> true;
> $should_handle_undefined_function(_, _) -> false.
>
> $handle_undefined_function(monitor, [process, Pid]) -> do_something();
> $handle_undefined_function(__demonitor, Pid) -> do_something_else().
>
> What do you think?

A function is dangerous in that it may return a value conditionally
(using for example the process dictionary, a receive, or anything else)
and this can heavily mess up the execution. You can still throw an undef
error yourself anywhere of course, but this one would look legit and
most confusing. That could lead to some debugging hell sessions. Perhaps
you do have a need for dynamically managing the list of allowed functions?

On the other hand, if you just return true or false right away, I don't
see what this has better compared to much shorter to write attributes.

I'm fine with both even though I favor attributes.

> Adding an attribute isn't hard, whether you generate code or not,
> and it can be used by tools to figure out what's going on with the
> module, providing better integration.
>
> And why not just use -export and still allow -spec anyway? The
> function is exported, has specs, but is just defined elsewhere. I'm
> sure there can be a simple solution that fits a lot better the
> existing toolbase.
>
> [1] The answer is: if the first argument to 'monitor' is anything
> other than the atom 'process', then an undef error will be triggered
> instead of 'badarg'.
>
> --
> Loïc Hoguin
> Erlang Cowboy
> Nine Nines
> http://ninenines.eu
> _________________________________________________
> erlang-questions mailing list
> erlang-q...@erlang.org <mailto:erlang-q...@erlang.org>
> http://erlang.org/mailman/__listinfo/erlang-questions
> <http://erlang.org/mailman/listinfo/erlang-questions>

Björn Gustavsson

unread,
Jan 22, 2013, 1:34:44 AM1/22/13
to Loïc Hoguin, erlang-questions
On Mon, Jan 21, 2013 at 7:04 PM, Loïc Hoguin <es...@ninenines.eu> wrote:
My proposal is to use an attribute to list the functions defined like this, so that error_handler can make the distinction between undef and badarg directly. The above function would then be written like this:

$handle_undefined_function(monitor, [process, Pid]) ->
    do_something();
$handle_undefined_function(demonitor, Pid) ->
    do_something_else().


I don't really get the rationale for your proposal.

If have understood you correctly, I must know beforehand
which functions that $handle_undefined_function would handle,
and put all functions to be handled in an attribute.

Why not use the existing -export() attribute and write like
this:

-export([monitor/2,demonitor/1]).

monitor(process, Pid) ->
   do_something.

demonitor(Pid) ->
   do_something_else().   

So what would be the advantage of your version?

--
Björn Gustavsson, Erlang/OTP, Ericsson AB

Loïc Hoguin

unread,
Jan 22, 2013, 6:10:56 AM1/22/13
to Björn Gustavsson, erlang-questions
On 01/22/2013 07:34 AM, Björn Gustavsson wrote:
> On Mon, Jan 21, 2013 at 7:04 PM, Loïc Hoguin <es...@ninenines.eu
> <mailto:es...@ninenines.eu>> wrote:
>
> My proposal is to use an attribute to list the functions defined
> like this, so that error_handler can make the distinction between
> undef and badarg directly. The above function would then be written
> like this:
>
> $handle_undefined_function(__monitor, [process, Pid]) ->
> do_something();
> $handle_undefined_function(__demonitor, Pid) ->

> do_something_else().
>
>
> I don't really get the rationale for your proposal.
>
> If have understood you correctly, I must know beforehand
> which functions that $handle_undefined_function would handle,
> and put all functions to be handled in an attribute.

Of course you need to know what functions exist in advance. How else do
you expect to be able to call them in a reliable manner?

> Why not use the existing -export() attribute and write like
> this:
>
> -export([monitor/2,demonitor/1]).
>
> monitor(process, Pid) ->
> do_something.
>
> demonitor(Pid) ->
> do_something_else().
>
> So what would be the advantage of your version?

It still allows Evan Miller to write this:

-attr([first_name/1, last_name/1, ...]).

$handle_undefined_function(F, [Model]) ->
{_, Value} = lists:keyfind(F, 1, Model),
Value.

As you can see he doesn't have to worry about adding clauses to handle
undef, which is good.

This also still allows you to do -extends:

%% Functions you override.
-export([ponies/1]).

%% Functions you don't.
-attr([horses/1, cows/1]).

$handle_undefined_function(F, Args) ->
apply(base_module, F, Args).

Again you still don't have to handle undef.

Tools can expect these functions. If you allow specs for undefined
functions then Dialyzer can also act as if they were defined normally.

Implementing it like this, without pushing the burden of handling undef
to the user, would avoid making half the community scream in terror and
endlessly ask for the removal of the feature (like with pmods).

Vlad Dumitrescu

unread,
Jan 22, 2013, 6:57:47 AM1/22/13
to Loïc Hoguin, erlang-questions
Hi,

On Tue, Jan 22, 2013 at 12:10 PM, Loïc Hoguin <es...@ninenines.eu> wrote:
On 01/22/2013 07:34 AM, Björn Gustavsson wrote:
Why not use the existing -export() attribute and write like
this:

-export([monitor/2,demonitor/1]).

monitor(process, Pid) ->
    do_something.

demonitor(Pid) ->
    do_something_else().

So what would be the advantage of your version?

It still allows Evan Miller to write this:

-attr([first_name/1, last_name/1, ...]).

$handle_undefined_function(F, [Model]) ->
    {_, Value} = lists:keyfind(F, 1, Model),
    Value.


I'm sorry, but I don't think you are answering Björn's question. For this particular case, why not write regular Erlang like

first_name(Model) -> get(first_name, Model).
last_name(Model) -> get(last_name, Model).
get(F, [Model]) ->
    {_, Value} = lists:keyfind(F, 1, Model),
    Value.
 
which is much clearer to understand. One argument might be that there is some duplication, but I think that it could be handled with much more elegance if we had abstract patterns implemented.

Do you have a more relevant example where using $handle_undefined_function gives significant advantage?

best regards,
Vlad

Loïc Hoguin

unread,
Jan 22, 2013, 7:02:10 AM1/22/13
to Vlad Dumitrescu, erlang-questions
On 01/22/2013 12:57 PM, Vlad Dumitrescu wrote:
> Hi,
>
> On Tue, Jan 22, 2013 at 12:10 PM, Loïc Hoguin <es...@ninenines.eu
> <mailto:es...@ninenines.eu>> wrote:
>
> On 01/22/2013 07:34 AM, Björn Gustavsson wrote:
>
> Why not use the existing -export() attribute and write like
>
> this:
>
> -export([monitor/2,demonitor/__1]).
>
> monitor(process, Pid) ->
> do_something.
>
> demonitor(Pid) ->
> do_something_else().
>
> So what would be the advantage of your version?
>
>
> It still allows Evan Miller to write this:
>
> -attr([first_name/1, last_name/1, ...]).
>
> $handle_undefined_function(F, [Model]) ->
> {_, Value} = lists:keyfind(F, 1, Model),
> Value.
>
>
> I'm sorry, but I don't think you are answering Björn's question. For
> this particular case, why not write regular Erlang like
>
> first_name(Model) -> get(first_name, Model).
> last_name(Model) -> get(last_name, Model).
> get(F, [Model]) ->
> {_, Value} = lists:keyfind(F, 1, Model),
> Value.
> which is much clearer to understand. One argument might be that there is
> some duplication, but I think that it could be handled with much more
> elegance if we had abstract patterns implemented.
>
> Do you have a more relevant example where using
> $handle_undefined_function gives significant advantage?

That's a question about $handle_undefined_function in general, not about
this specific implementation proposal. Björn is the one who wrote the
current implementation that's now in master, I don't have to tell him
what can be done with it, he knows. I do have to tell him how
implementing it this way improves the usage, though. Which is the sole
reason of this thread.

Vlad Dumitrescu

unread,
Jan 22, 2013, 7:10:49 AM1/22/13
to Loïc Hoguin, erlang-questions
Maybe I'm confused, but your answer would make 100% sense if Björn's question would have been

"
  Why not use the existing -export() attribute and write like this:
        -export([monitor/2,demonitor/1]).
        $handle_undefined_function(monitor, [process, Pid]) ->
            do_something();
        $handle_undefined_function(demonitor, Pid) ->
            do_something_else().
"

My question is a follow-up on his own formulation, where your suggestion is compared with an implementation not using $handle_undefined_function at all.

best regards,
Vlad

Loïc Hoguin

unread,
Jan 22, 2013, 7:25:59 AM1/22/13
to Vlad Dumitrescu, erlang-questions
On 01/22/2013 01:10 PM, Vlad Dumitrescu wrote:
> On Tue, Jan 22, 2013 at 1:02 PM, Loïc Hoguin <es...@ninenines.eu
> <mailto:es...@ninenines.eu>> wrote:
>
> On 01/22/2013 12:57 PM, Vlad Dumitrescu wrote:
>
> Hi,
>
> On Tue, Jan 22, 2013 at 12:10 PM, Loïc Hoguin
> <es...@ninenines.eu <mailto:es...@ninenines.eu>
> <mailto:es...@ninenines.eu <mailto:es...@ninenines.eu>>> wrote:
>
> On 01/22/2013 07:34 AM, Björn Gustavsson wrote:
>
> Why not use the existing -export() attribute and write like
>
> this:
>
> -export([monitor/2,demonitor/____1]).
> $handle_undefined_function(__monitor, [process, Pid]) ->
> do_something();
> $handle_undefined_function(__demonitor, Pid) ->
> do_something_else().
> "
>
> My question is a follow-up on his own formulation, where your suggestion
> is compared with an implementation not using $handle_undefined_function
> at all.

And I answered it based on known potential uses. The snippet you quote
was made to demonstrate the flaws in the current implementation, not to
demonstrate an interesting use of the feature. It can be demonstrated
using Evan's example too, for example:

$handle_undefined_function(F, [Model]) when is_list(Model) ->
{_, Value} = lists:keyfind(F, 1, Model),
Value;
$handle_undefined_function(F, Args) ->
error_handler:raise_undef_exception(?MODULE, F, Args).

This would be an incorrect implementation of Evan's use for it, and
nothing about it says "It's obvious!". And yet it's still wrong. Correct
implementation would be:

$handle_undefined_function(F, [Model]) when is_list(Model) ->
{_, Value} = lists:keyfind(F, 1, Model),
Value;
$handle_undefined_function(F, Args) ->
case lists:member({F, length(Args)},
[{first_name, 1}, {last_name, 1}, ...]) of
true -> erlang:error(badarg);
false -> error_handler:raise_undef_exception(?MODULE, F, Args)
end.

With my proposal he can write:

-attr([first_name/1, last_name/1]).

$handle_undefined_function(F, [Model]) when is_list(Model) ->
{_, Value} = lists:keyfind(F, 1, Model),
Value.

This makes error_handler properly handle undef errors, reduces the code
needed to use the feature, allow tools to behave nicely when using the
feature, etc.

As for why Evan would use $handle_undefined_function instead of creating
many functions, that's for him to decide. But both implementations of
$handle_undefined_function allow all known uses of the feature, yet only
one gets error handling and tools support straight.

Vlad Dumitrescu

unread,
Jan 22, 2013, 7:30:30 AM1/22/13
to Loïc Hoguin, erlang-questions
Ok, I see. I had lost track of who suggested the example originally.

I suppose that the functions listed in -attr() would have to be included in an -export() too if they are to be public?

regards,
Vlad

Loïc Hoguin

unread,
Jan 22, 2013, 7:34:06 AM1/22/13
to Vlad Dumitrescu, erlang-questions
I don't think they can appear as exported, due to error_handler's use of
function_exported to know if it's defined. The attribute can be named
something like -undef-export([]) though, to make it clearer. Note that
the feature is only applicable when the function is called through
M:F(...), not local calls, because the module wouldn't compile.

On 01/22/2013 01:30 PM, Vlad Dumitrescu wrote:
> Ok, I see. I had lost track of who suggested the example originally.
>
> I suppose that the functions listed in -attr() would have to be included
> in an -export() too if they are to be public?
>
> regards,
> Vlad
>
>
> On Tue, Jan 22, 2013 at 1:25 PM, Loïc Hoguin <es...@ninenines.eu
> <mailto:es...@ninenines.eu>> wrote:
>
> On 01/22/2013 01:10 PM, Vlad Dumitrescu wrote:
>
> On Tue, Jan 22, 2013 at 1:02 PM, Loïc Hoguin <es...@ninenines.eu
> <mailto:es...@ninenines.eu>
> <mailto:es...@ninenines.eu <mailto:es...@ninenines.eu>>> wrote:
>
> On 01/22/2013 12:57 PM, Vlad Dumitrescu wrote:
>
> Hi,
>
> On Tue, Jan 22, 2013 at 12:10 PM, Loïc Hoguin
> <es...@ninenines.eu <mailto:es...@ninenines.eu>
> <mailto:es...@ninenines.eu <mailto:es...@ninenines.eu>>
> <mailto:es...@ninenines.eu <mailto:es...@ninenines.eu>
> <mailto:es...@ninenines.eu <mailto:es...@ninenines.eu>>>> wrote:
>
> On 01/22/2013 07:34 AM, Björn Gustavsson wrote:
>
> Why not use the existing -export() attribute
> and write like
>
> this:
>
> -export([monitor/2,demonitor/______1]).
> -export([monitor/2,demonitor/__1]).
> $handle_undefined_function(____monitor, [process, Pid]) ->
> do_something();
> $handle_undefined_function(____demonitor, Pid) ->
>
> do_something_else().
> "
>
> My question is a follow-up on his own formulation, where your
> suggestion
> is compared with an implementation not using
> $handle_undefined_function
> at all.
>
>
> And I answered it based on known potential uses. The snippet you
> quote was made to demonstrate the flaws in the current
> implementation, not to demonstrate an interesting use of the
> feature. It can be demonstrated using Evan's example too, for example:
>
> $handle_undefined_function(F, [Model]) when is_list(Model) ->
>
> {_, Value} = lists:keyfind(F, 1, Model),
> Value;
>
> $handle_undefined_function(F, Args) ->
> error_handler:raise_undef___exception(?MODULE, F, Args).
>
> This would be an incorrect implementation of Evan's use for it, and
> nothing about it says "It's obvious!". And yet it's still wrong.
> Correct implementation would be:
>
> $handle_undefined_function(F, [Model]) when is_list(Model) ->
>
> {_, Value} = lists:keyfind(F, 1, Model),
> Value;
> $handle_undefined_function(F, Args) ->
> case lists:member({F, length(Args)},
> [{first_name, 1}, {last_name, 1}, ...]) of
> true -> erlang:error(badarg);
> false -> error_handler:raise_undef___exception(?MODULE, F,

Björn Gustavsson

unread,
Jan 22, 2013, 9:41:16 AM1/22/13
to Loïc Hoguin, erlang-questions
On Tue, Jan 22, 2013 at 1:02 PM, Loïc Hoguin <es...@ninenines.eu> wrote:
That's a question about $handle_undefined_function in general, not about this specific implementation proposal. Björn is the one who wrote the current implementation that's now in master, I don't have to tell him what can be done with it, he knows. I do have to tell him how implementing it this way improves the usage, though. Which is the sole reason of this thread.


Except that your "improvements" to $handle_undefined_function
renders it completely useless for the problem it was supposed
to solve.

Namely to emulate the existing -extends() directive, which is
used as follows:

-extends(Module).

where Module is the module to be extended. [1]

Your mission, should you decide to accept it,
is to implement -extends(Module) in a parse transform.

You have no knowledge of the the other module. It might
not even exist when the module containing the -extends()
directive is compiled; furthermore, if more functions are
added to the module being extended in the future, you
should still be able to call them from the module
containing the -extends() directive (without recompiling
it).

To make it more concrete, you should implement a
parse transform and use it to compile the following
module:

-module(extender).
-extends(mission_impossible).

extender should then be able to call any function in the
module 'mission_impossible' stored on my computer.
That is:

  extender:foo(42)

will call mission_impossible:foo(42) provided that
foo/1 is exported from mission_impossible.

You may use either the current implementation or
your "improved" version of $handle_undefined_function,
at your choice.

Should any of your computer systems crash or burn,
the Secretary will disavow any knowledge of your
actions. [2]


Loïc Hoguin

unread,
Jan 22, 2013, 10:07:34 AM1/22/13
to Björn Gustavsson, erlang-questions
On 01/22/2013 03:41 PM, Björn Gustavsson wrote:
> On Tue, Jan 22, 2013 at 1:02 PM, Loïc Hoguin <es...@ninenines.eu
> <mailto:es...@ninenines.eu>> wrote:
>
> That's a question about $handle_undefined_function in general, not
> about this specific implementation proposal. Björn is the one who
> wrote the current implementation that's now in master, I don't have
> to tell him what can be done with it, he knows. I do have to tell
> him how implementing it this way improves the usage, though. Which
> is the sole reason of this thread.
>
>
> Except that your "improvements" to $handle_undefined_function
> renders it completely useless for the problem it was supposed
> to solve.
>
> Namely to emulate the existing -extends() directive, which is
> used as follows:
>
> -extends(Module).
>
> where Module is the module to be extended. [1]
>
> Your mission, should you decide to accept it,
> is to implement -extends(Module) in a parse transform.
>
> You have no knowledge of the the other module.

This statement alone makes me go back to thinking -extends() should be
dropped, and if not, $handle_undefined_function shouldn't be used and
should be dropped; it's incompatible with tools, prone to user error,
and is going to make half the community scream in terror.

As Jose Valim said right after you, the parse transform can simply look
at the original module directly. If it can't, then there's no fixing it.

Tony Rogvall

unread,
Jan 22, 2013, 10:39:41 AM1/22/13
to Loïc Hoguin, erlang-questions
Regardless of -extends() should be dropped or not I think $handle_undefined_function is a great addition
to the runtime system. It allows for various experiments and hacks. No one forces you or any other one to
use it. So please just look in an other direction for a while. Screaming in terror? Stop being such a wimp.

/Tony


This statement alone makes me go back to thinking -extends() should be dropped, and if not, $handle_undefined_function shouldn't be used and should be dropped; it's incompatible with tools, prone to user error, and is going to make half the community scream in terror.

As Jose Valim said right after you, the parse transform can simply look at the original module directly. If it can't, then there's no fixing it.

--
Loïc Hoguin
Erlang Cowboy
Nine Nines
http://ninenines.eu
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions

Loïc Hoguin

unread,
Jan 22, 2013, 11:05:21 AM1/22/13
to Tony Rogvall, erlang-questions
You missed the humor.

No one forces me, or anyone else, to use it. Indeed we can recommend
people not use it and tell people to avoid using libraries that are
using it, and if they still do tell them to *really* be careful about
it. All this not because we think the *feature* is bad, but because the
*implementation* can easily lead to user error.

Ulf Wiger

unread,
Jan 22, 2013, 11:11:38 AM1/22/13
to Loïc Hoguin, erlang-questions

On 22 Jan 2013, at 17:05, Loïc Hoguin wrote:

> No one forces me, or anyone else, to use it. Indeed we can recommend people not use it and tell people to avoid using libraries that are using it, and if they still do tell them to *really* be careful about it. All this not because we think the *feature* is bad, but because the *implementation* can easily lead to user error.

This happens to be the exact same advice as for the original error_handler and libraries that rely on error_handler modifications - although the risk of collateral damage is much smaller with $handle_undefined_function...

BR,
Ulf W

Ulf Wiger, Co-founder & Developer Advocate, Feuerlabs Inc.
http://feuerlabs.com

Evan Miller

unread,
Jan 22, 2013, 1:30:45 PM1/22/13
to Loïc Hoguin, erlang-questions
On Tue, Jan 22, 2013 at 6:25 AM, Loïc Hoguin <es...@ninenines.eu> wrote:
>
> As for why Evan would use $handle_undefined_function instead of creating many functions, that's for him to decide. But both implementations of $handle_undefined_function allow all known uses of the feature, yet only one gets error handling and tools support straight.
>

Since my name is being thrown around as a sort of bugaboo and a way to
scare programmers' children with night-time stories, I'd like to
clarify why exactly I support horrible features like Pmods and
$handle_undefined_function in the Erlang run-time.

Most people on this list seem to care about "big-scale" issues like
reliability, predictability, processing kajillions of commercial
transactions, and so forth. I happen to care a lot more about
"small-scale" issues -- in particular, the programming experience of
one person just trying to get something to work for the first time and
having the code look nice. I think that's ultimately where innovation
comes from -- one person experimenting around, seeing if things work,
and not caring whether it's production-ready or fit to be seen by
anyone.

I worry that Erlang is missing out on a lot of potential developer
talent because the platform's benefits tend to be back-loaded: it's
great for the big-scale stuff, but it can be painful for a newcomer
just tinkering around and seeing if stuff works. A hobbyist has to be
both extremely tenacious and perhaps delusional to stick with Erlang
long enough to realize the platform's benefits.

For example, Erlang is missing one feature found in every other
language that I've used that is missing from Erlang. It's the ability
to write:

Person.name

Not Person#person.name, not person:name(Person), not
proplists:get_value, not dict:fetch, just plain old stupid
anyone-who-has-ever-looked-at-a-programming-language-before-can-understand:
Person Dot Name. Every other language I can think of has something
like this, whether it's person.name, person->name, $person{name}, or
person.name(). I wonder how many potential Erlang programmers moved on
to something else because they couldn't find this very simple language
construct while they were playing around with Erlang for the first
time.

Thanks to Pmods and some code generation, I was able to come up with
what I felt was acceptable alternative in Erlang: Person:name(). If
Erlang ever gets any kind of native support for syntax that looks like
that, I honestly won't care so much about Pmods.

That brings me to $handle_undefined_function. I won't actually use it
in production (much), since I'd rather have compile-time checks for my
generated functions, and I have most of the code generation I need at
this point. But it would have made my life a lot easier while I was
prototyping the library that later became BossDB. So that's why I
think it's necessary: to make it easier to experiment with ideas
before sweating it out with the dark arts of code generation.

I'd like to reiterate my proposal for
$should_handle_undefined_function as a way to resolve Loïc's concerns
while preserving the dynamic nature of $handle_undefined_function.
Some interesting use-cases that come to mind for me are a dynamic API
like:

person:find_by_name("Joseph")

Is that a good API? I don't know, but I'd like to be able to
experiment with it, get a feel for it, and move on to something else
quickly if it turns out to be a bad idea. I'm sure people will
immediately tell me that the "right" way to do it is find(person,
name, "Joseph") or something. But maybe it's not -- maybe the increase
in readability is worth the loss in generality. Readability and
familiarity are really important for attracting hobbyist programmers,
who have a lot to contribute to any platform, and whose interests I
try represent on this list as best I can.

Evan

>
>
> --
> Loïc Hoguin
> Erlang Cowboy
> Nine Nines
> http://ninenines.eu
> _______________________________________________
> erlang-questions mailing list
> erlang-q...@erlang.org
> http://erlang.org/mailman/listinfo/erlang-questions




--
Evan Miller
http://www.evanmiller.org/

Tristan Sloughter

unread,
Jan 22, 2013, 2:09:40 PM1/22/13
to Evan Miller, erlang-questions
Honestly this sounds like wanting Elixir. 

ChicagoBoss and BossDB would still have the benefits of the Erlang VM and also have the style of coding you are looking for.

Tristan

Yurii Rashkovskii

unread,
Jan 22, 2013, 5:48:07 PM1/22/13
to erlang-pr...@googlegroups.com, erlang-questions
Elixir doesn't have any other "undefined function" handling beyond of what Erlang/OTP provides.

Garrett Smith

unread,
Jan 22, 2013, 6:06:19 PM1/22/13
to Yurii Rashkovskii, erlang-pr...@googlegroups.com, erlang-questions
Looking over Elixir, I tend to agree with Tristan -- it seems to have
all the syntactic sugar that creative, non-hardcore, easily frightened
developers might like :)

In seriousness, it looks like a nice language -- I wonder if it, or
the Ruby, Python, JavaScript "front-end" toolsets, might be a better
fit for extending Erlang applications to programmers who are looking
for a kinder, gentler syntax?

Yurii Rashkovskii

unread,
Jan 22, 2013, 6:11:18 PM1/22/13
to erlang-pr...@googlegroups.com, Yurii Rashkovskii, erlang-questions
It isn't really much about syntax. It is, however, about 1) macro capabilities, 2) saner and a more consistent stdlib (we already have a fairly good and fast Unicode implementation and a faster hash dictionary)

Loïc Hoguin

unread,
Jan 22, 2013, 6:16:15 PM1/22/13
to Yurii Rashkovskii, erlang-questions
I can understand 1), but what's the point of 2)? Wouldn't it be better
for that to be in the erlang/otp repository instead so that everything
can benefit from it?
> >>> Not Person#person.name <http://person.name>, not
> person:name(Person), not
> >>> proplists:get_value, not dict:fetch, just plain old stupid
> >>>
> >>>
> anyone-who-has-ever-looked-at-a-programming-language-before-can-understand:
>
> >>> Person Dot Name. Every other language I can think of has something
> >>> like this, whether it's person.name <http://person.name>,
> person->name, $person{name}, or
> >>> person.name <http://person.name>(). I wonder how many potential
> > erlang-q...@erlang.org <javascript:>
> > http://erlang.org/mailman/listinfo/erlang-questions
> <http://erlang.org/mailman/listinfo/erlang-questions>
> >
> _______________________________________________
> erlang-questions mailing list
> erlang-q...@erlang.org <javascript:>
> http://erlang.org/mailman/listinfo/erlang-questions
> <http://erlang.org/mailman/listinfo/erlang-questions>
>
>
>
> _______________________________________________
> erlang-questions mailing list
> erlang-q...@erlang.org
> http://erlang.org/mailman/listinfo/erlang-questions
>


Yurii Rashkovskii

unread,
Jan 22, 2013, 6:29:12 PM1/22/13
to erlang-pr...@googlegroups.com, Yurii Rashkovskii, erlang-questions
Many reasons. 

Certain API unifications (like 0-based index) are hard to do for element/setelement — and you can't override those anyway — as it will break compatibility. 
Certain API extensions are much, much easier and enjoyable in Elixir as opposed to Erlang (see unicode.ex)
Designing an API that benefits from agreed-on primitives in Elixir won't be possible in Erlang so Elixir would have to use a slightly inferior base.
Elixir contributors like programming in Elixir :)

Yurii.

Loïc Hoguin

unread,
Jan 22, 2013, 6:32:47 PM1/22/13
to Yurii Rashkovskii, erlang-questions
I thought Elixir compiled to Erlang (and not even Core Erlang) anyway?
How can there be any primitive that aren't available in Erlang? The API,
sure, but most of Unicode has nothing to do with an API.

> > >>> person.name <http://person.name> <http://person.name>().

Yurii Rashkovskii

unread,
Jan 22, 2013, 6:34:41 PM1/22/13
to erlang-pr...@googlegroups.com, Yurii Rashkovskii, erlang-questions
I apologize, I meant library primitives, such as use of protocols or Elixir records.

Elixir compiles to Erlang AST.

Also, writing unicode.ex is a PITA. Possible (of course!) but PITA.

José Valim

unread,
Jan 22, 2013, 6:50:56 PM1/22/13
to Loïc Hoguin, Yurii Rashkovskii, erlang-questions
I can understand 1), but what's the point of 2)? Wouldn't it be better for that to be in the erlang/otp repository instead so that everything can benefit from it?

Loic, whenever it makes sense, I contribute patches back to OTP. :)

I would, for example, contribute our dict implementation, but I don't believe OTP is looking for another dict implementation (I am going to write a blog post with details and benchmarks soon for those interested).

Some of the stuff in our stdlib, like providing zero-index based access everywhere is hard to "backport" to Erlang. And other stuff like the Enum module, which is a bunch of functions meant to work on any enumerable data structure, like lists, dicts, etc, requires protocols.

In any case, if you need/want to use any of Elixir modules from your Erlang code, you can just drop them into your project. For example, you can use our strings module, that is based on the Unicode Standarded, straight from Erlang:

    'Elixir-String':capitalize(<<"fin">>)
    <<"Fin">>

I agree 'Elixir-String' is not the prettiest sight but that is because we could not use 'Elixir.String' due to packages (RIP). I hope we can revert this decision a while after R16 is out and make integration a bit more pleasant.

In general, I am very excited about R16. It will enable us to use cover in Elixir, ansi codes and more. And feel free to call me on anything you believe I should be contributing to OTP directly.

Richard O'Keefe

unread,
Jan 22, 2013, 9:57:43 PM1/22/13
to Tony Rogvall, erlang-questions

On 23/01/2013, at 4:39 AM, Tony Rogvall wrote:

> Regardless of -extends() should be dropped or not I think $handle_undefined_function is a great addition
> to the runtime system. It allows for various experiments and hacks.

And it *prevents* other experiments and hacks.

There is no such thing as a feature that is not a limitation.

Richard O'Keefe

unread,
Jan 22, 2013, 11:01:27 PM1/22/13
to Evan Miller, erlang-questions

On 23/01/2013, at 7:30 AM, Evan Miller wrote:
>
> Most people on this list seem to care about "big-scale" issues like
> reliability, predictability, processing kajillions of commercial
> transactions, and so forth. I happen to care a lot more about
> "small-scale" issues -- in particular, the programming experience of
> one person just trying to get something to work for the first time and
> having the code look nice.

I care a heck of a lot about small scale usability issues,
and that's precisely why I DISLIKE pmods -extends and so on.
Because I have learned from painful experience that reliability
and predictability ARE small scale usability issues.

The fact that -extends as currently conceived destroys our
ability to know what a module exports, well, words fail me.
It's such a *TERRIBLE* thing to do to plain simple-minded
programmers like me that it makes me want to smash things in
anger. I make mistakes *all the time*, and not being able to
ask "have I actually defined everything that is either called
or exported" any more, well, how does THAT help me?

Let's face it, I can get pretty close to the effect of
-extends WITHOUT it. I happen to have a small Erlang file
that lets me type
> export_from:module(foobar).
in an Erlang shell and get an export list and a bunch of
glue definitions that I can paste into a source file.
It's careful to exclude module_info/[0,1], and it uses
the io module rather clumsily. And it's under 50 SLOC.

Writing a parse transform that gets the info from a .beam file
would be harder, but easier for a programmer to use. I'd rather
not do that, because simply saying "re-export EVERYTHING" is too
dangerous.

-export_from(Module, Functors).

which I think I first proposed back in about 1996, permits so
much more checking, and makes life SO much nicer for the small-
scale programmer, that the contrast is almost dizzying.

> I think that's ultimately where innovation
> comes from -- one person experimenting around, seeing if things work,
> and not caring whether it's production-ready or fit to be seen by
> anyone.

Right. And *not* from shoving half-baked ideas out the door
too early. Quintus did that once, and ever since Prolog programmers
have been using an I/O approach that was significantly inefficient and
clumsy compared with what we _could_ have had if the ***** who shoved
it out the door had asked around the company if anyone had a better
idea. The botch even made it into the standard! (Heck, it even got
copied into Erlang...)

> For example, Erlang is missing one feature found in every other
> language that I've used that is missing from Erlang. It's the ability
> to write:
>
> Person.name

That's why Joe Armstrong and I have both proposed essentially the same
data structure (in my case, complete with details of how it can be
implemented and actual measurements from a pilot implementation in a
micro-VM) many years ago.

However, I would point out that this feature requires EITHER
a static type system (Pascal, C, C++, Oberon, Java, C#, &c)
OR a run-time search to find where the field is.

> Every other language I can think of has something
> like this,

There are vastly more languages than you appear to be aware of.
The trick is to make something like this
- work EFFICIENTLY
- WITHOUT a strong static type system.

> person.name(). I wonder how many potential Erlang programmers moved on
> to something else because they couldn't find this very simple language
> construct while they were playing around with Erlang for the first
> time.

Only the hard-of-thinking who were unwilling to learn a new way to
program.
>
> Thanks to Pmods and some code generation, I was able to come up with
> what I felt was acceptable alternative in Erlang: Person:name().

Did you ever measure how long that took?
Compared with Person#person_name?
Here's a hint: if you are sick of having programs
run too fast, I've some old machines I could sell you.
There are easier ways to go slow than abusing the language.

Erlang is *NOT* an object oriented language.
That's deliberate: the inventors of Erlang were well aware of OO
and chose not to use it.

If you try to use Erlang as if it were an object oriented language,
you are wasting your time. F# will do a _much_ better job of
satisfying you, and it's a perfectly fine language available under
Mono as well as .NET.

Kenneth Lundin

unread,
Jan 23, 2013, 6:39:25 AM1/23/13
to Richard O'Keefe, erlang-questions
Thank you for all input in this matter. I mean both regarding -extends and $handle_undefined_function.
 
I think it is place for some clarifications which will have implications on earlier statements in this thread.
 
On October 19:th a decision from the OTP Technical Board (a fora internal to the
OTP development team at Ericsson) regarding parameterized types was announced.
 
The announcement looked like this:
Issue 4 - What to do with unsupported feature 'parameterized modules'.

- The board acknowledges that a lot of software relies on this feature although it's always been experimental. The current form of the implementation is not acceptable and the parameterized modules as such has never been accepted as a feature in the language. The feature is also not compatible with for example module-fun's and is not fully integrated with the rest of the tools in OTP.

To allow for source code compatibility, the board decides to only remove the syntactic support for parameterized modules. This means:

* Apply with tuple modules will be retained, supported and properly documented.

* Error handler functionality that today supports inheritance will be slightly generalized, so that parameterized modules can contain stubs to facilitate inheritance. The functionality in the error handler will be documented.

* The parser will accept the syntax for implementation of parameterized modules, but if the parameterized modules are not handled by a parse_transform, an error message will be generated at a later stage (this concerns the implementation of the modules themselves only).

* A parse_transform handling source code implementing a parameterized module will be published in the public domain, free to use and include in any package needing it. It will however not be part of OTP. As it uses supported and documented functionality, it will however be expected to work for forthcoming releases as well, or at least to be possible to adopt to forthcoming releases.

As apply with tuple modules is retained, calls to parameterized modules will remain as they look today. Only the implementation of the parameterized modules themselves need to apply a parse transform to allow for the "unsupported" syntax extensions. Calling a function in an instance of a parameterized module from an evaluator (e.g. the shell) will also work as before.

 

As I remember it we got mainly positive reactions regarding this decision and now we have implemented it exactly as said and it will be released in R16A (beta release on January 30:th) and R16B (product release on Feb 27:th).

In short the previously undocumented support for parameterized modules is removed from the OTP distribution and is replaced with a parse transform which we will provide as a separate project at Github. We do this to make the transition smooth for projects having used the parameteized modules feature. We will not continue to support or enhance that parse transform in the future (it is up to the users interested in the feature to do that).

Some of the comments in this thread are related to how parmaterized modules and
'-extends' in particular could be improved, changed etc. and it is perfectly ok to do that, but it is not part of Erlang/OTP any more. 

As a consequence of removing parameterized modules from OTP we have changed the error_handler by removing a built in knowledge about parameterized modules and by adding a more general and useful functionality, the $handle_undefined_function feature.

This feature is useful in a number of situations where one was earlier forced to modify the global error_handler:

- Can support the parse-transform for parameterized modules (the -extends attribute)

- Can be used to safely (or more safely than before) implement various "mocking" functionality for testing purposes

- Can be used to interface external generic RPC interfaces like the android case.

It will be documented in the error_handler module together with a fat warning that you should not use this in normal production code etc. The error_handler module is hardly something that the average or novice programmer will stumble into and immediately use features from.

So in summary we have done what we promised to do:

- taken parmeterized modules out of Erlang/OTP (offering a parse_transform instead)

- removed knowledge about parameterized modules from error_handler and added a more generic and safe way for extending the error_handler per module instead.

 

/Regards Kenneth, Erlang/OTP Ericsson

 

 

Tony Rogvall

unread,
Jan 23, 2013, 9:13:07 AM1/23/13
to Richard O'Keefe, erlang-questions

On 23 jan 2013, at 03:57, Richard O'Keefe <o...@cs.otago.ac.nz> wrote:

>
> On 23/01/2013, at 4:39 AM, Tony Rogvall wrote:
>
>> Regardless of -extends() should be dropped or not I think $handle_undefined_function is a great addition
>> to the runtime system. It allows for various experiments and hacks.
>
> And it *prevents* other experiments and hacks.
>
Why? What kind of experiments and hacks are we talking about here?
Sure, I can think of some, but who is to decide what experiments are worth doing ?
( I just hope it is OTP in this case, do not chicken out ;)

> There is no such thing as a feature that is not a limitation.
>
And? If "users" do not add scary features like the '$handle_undefined_function' functions
in their modules they will be safe. if they ignore good advise they do void the guarantee.
But that, I think, is the glory of things.

/Tony

Robert Virding

unread,
Jan 24, 2013, 12:12:12 PM1/24/13
to José Valim, Yurii Rashkovskii, erlang-questions
From: "José Valim" <jose....@plataformatec.com.br>


I can understand 1), but what's the point of 2)? Wouldn't it be better for that to be in the erlang/otp repository instead so that everything can benefit from it?

Loic, whenever it makes sense, I contribute patches back to OTP. :)

I would, for example, contribute our dict implementation, but I don't believe OTP is looking for another dict implementation (I am going to write a blog post with details and benchmarks soon for those interested).

If you have a better/faster implementation of dict with the same API then you should definitely contribute it. The original idea behind having multiple dict (and sets) libraries was to allow the user to choose the one that was best for their application. The internals of dict were deliberately unspecified to allow the implementation to change in the future. By having other versions of dict with different properties but the same API then users could choose. NO, I don't like having an option to new() to say which one you want as then you automatically limit the options, having different modules makes it easy to add new ones and to write your own! So hand it in and may the fastest win.



Some of the stuff in our stdlib, like providing zero-index based access everywhere is hard to "backport" to Erlang. And other stuff like the Enum module, which is a bunch of functions meant to work on any enumerable data structure, like lists, dicts, etc, requires protocols.

What is it with zero-based indexes that make people so morbidly fascinated by them. If you are talking *offsets* then I agree that zero-based is fine, it's saying how far away something is from a some point. But here we are talking *indexes*, you know like first, second, third, etc. No indexes start at one! And don' go on about how C does it because C doesn't have arrays and indexes it has pointers and offsets, foo[3] is just syntactic sugar for *foo+3. And don't go on about how much easier it is to count from zero, I don't buy that, we can all add and subtract one without problems. Or at least I hope so.

I think the OTP group made a bad decision *indexing* binaries from zero instead of one like the rest of erlang. It is both wrong and makes the system inconsistent.

So to go on about this but there are some sensible things you can criticize Erlang for and indexing from one is not one of them. (And not really the syntax either, and this comes from the creator of LFE)

Robert

Loïc Hoguin

unread,
Jan 24, 2013, 12:21:43 PM1/24/13
to Robert Virding, erlang-questions, José Valim, Yurii Rashkovskii
On 01/24/2013 06:12 PM, Robert Virding wrote:
> Some of the stuff in our stdlib, like providing zero-index based
> access everywhere is hard to "backport" to Erlang. And other stuff
> like the Enum module, which is a bunch of functions meant to work on
> any enumerable data structure, like lists, dicts, etc, requires
> protocols.
>
>
> What is it with zero-based indexes that make people so morbidly
> fascinated by them. If you are talking *offsets* then I agree that
> zero-based is fine, it's saying how far away something is from a some
> point. But here we are talking *indexes*, you know like first, second,
> third, etc. No indexes start at one! And don' go on about how C does it
> because C doesn't have arrays and indexes it has pointers and offsets,
> foo[3] is just syntactic sugar for *foo+3. And don't go on about how
> much easier it is to count from zero, I don't buy that, we can all add
> and subtract one without problems. Or at least I hope so.

There are only two hard problems in computer science: cache
invalidation, naming things, and off by one errors.

> I think the OTP group made a bad decision *indexing* binaries from zero
> instead of one like the rest of erlang. It is both wrong and makes the
> system inconsistent.

But then you can write:

<< _:Pos/binary, C, _/binary >>.

Alternative would be to -1/+1 all the time.

--
Loïc Hoguin
Erlang Cowboy
Nine Nines
http://ninenines.eu

Björn-Egil Dahlberg

unread,
Jan 24, 2013, 12:31:27 PM1/24/13
to erlang-q...@erlang.org
On 2013-01-24 18:12, Robert Virding wrote:

Some of the stuff in our stdlib, like providing zero-index based access everywhere is hard to "backport" to Erlang. And other stuff like the Enum module, which is a bunch of functions meant to work on any enumerable data structure, like lists, dicts, etc, requires protocols.

What is it with zero-based indexes that make people so morbidly fascinated by them. If you are talking *offsets* then I agree that zero-based is fine, it's saying how far away something is from a some point. But here we are talking *indexes*, you know like first, second, third, etc. No indexes start at one! And don' go on about how C does it because C doesn't have arrays and indexes it has pointers and offsets, foo[3] is just syntactic sugar for *foo+3. And don't go on about how much easier it is to count from zero, I don't buy that, we can all add and subtract one without problems. Or at least I hope so.

I think the OTP group made a bad decision *indexing* binaries from zero instead of one like the rest of erlang. It is both wrong and makes the system inconsistent.

I can see your point. I tend to think of indexing binaries as "indexing char arrays" and everything else as "indexing erlang terms".



So to go on about this but there are some sensible things you can criticize Erlang for and indexing from one is not one of them. (And not really the syntax either, and this comes from the creator of LFE)

IMHO: I think not having zero-based indexing in Erlang was a *big* mistake. I can see where it comes from, but still .. it's a pain knowing that I have to do +1/-1 to make certain algorithms function properly.

Btw, when you say "the OTP group made a bad decision", exactly who are you referring too? Yourself? Isn't these decisions from CS-lab? Certainly way before my time =)

// Björn-Egil

Björn-Egil Dahlberg

unread,
Jan 24, 2013, 12:47:18 PM1/24/13
to erlang-q...@erlang.org
On 2013-01-24 18:31, Björn-Egil Dahlberg wrote:
On 2013-01-24 18:12, Robert Virding wrote:
I think the OTP group made a bad decision *indexing* binaries from zero instead of one like the rest of erlang. It is both wrong and makes the system inconsistent.
Btw, when you say "the OTP group made a bad decision", exactly who are you referring too? Yourself? Isn't these decisions from CS-lab? Certainly way before my time =)
That probably sounds a bit more irritated than intended.

I'm honestly curious, who decided indexing on terms and binaries and what was the reasoning behind it?

I think I recall someone saying that binary-syntax was the last thing out from CS-Lab?

// Björn-Egil


Rustom Mody

unread,
Jan 24, 2013, 1:23:10 PM1/24/13
to Loïc Hoguin, Erlang Users' List
On Thu, Jan 24, 2013 at 10:51 PM, Loïc Hoguin <es...@ninenines.eu> wrote:

What is it with zero-based indexes that make people so morbidly
fascinated by them. If you are talking *offsets* then I agree that
zero-based is fine, it's saying how far away something is from a some
point. But here we are talking *indexes*, you know like first, second,
third, etc. No indexes start at one! And don' go on about how C does it
because C doesn't have arrays and indexes it has pointers and offsets,
foo[3] is just syntactic sugar for *foo+3. And don't go on about how
much easier it is to count from zero, I don't buy that, we can all add
and subtract one without problems. Or at least I hope so.

There are only two hard problems in computer science: cache invalidation, naming things, and off by one errors.


I can drive a car with European controls or one with American controls.
And yet whenever I change over, I end up turning on the wiper when I want to turn on the turning lights.

Its fine to make fun of people who fixate over 0/1 based indexing.
Having both in the same language is really an invitation to chaos.
We are seeing that in our project where the initial list based prototype is now being replaced by an array based one.

How much time is spent on this debugging is left to the imagination!

"Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration." -- Stan Kelly-Bootle

Richard O'Keefe

unread,
Jan 24, 2013, 8:59:20 PM1/24/13
to Loïc Hoguin, José Valim, Yurii Rashkovskii, erlang-questions
> On 01/24/2013 06:12 PM, Robert Virding wrote:
>>
>> What is it with zero-based indexes that make people so morbidly
>> fascinated by them.

Dijkstra has explained this at some length in one of his papers.

As for me, I find that working with half-open [Start,End) intervals
in arrays substantially reduces the rate at which I make off-by-one
errors.

I've built a Smalltalk system, complete with 1-origin indexing
everywhere. The one thing that saves me from errors is the Smalltalk
practice of using higher-order whole-collection operations that don't
mention indices at all.

When I designed the string processing library in Quintus Prolog,
I chose to avoid indices everywhere that I possibly could and to
specify the substring B in ABC as

substring(Whole, Part, LenA, LenB, LenC)

where Prolog could of course solve for any of LenA, LenB, LenC.
So I could say "4 characters 3 before the end" _,4,3 or
"everything but the first 2 and last 5" 2,_,5 or "skip 7 and
take 9" 7,9,_ or even let the predicate find things by
searching.

It was clearly documented that all these and similar arguments
were *lengths* of substrings.

And I got complaints about using 0-origin indexing, when none
of these arguments was an index!

Robert Virding

unread,
Jan 25, 2013, 7:11:42 AM1/25/13
to Björn-Egil Dahlberg, erlang-q...@erlang.org
The decisions were made at different times.

The choice of indexing in tuples and lists (with the lists:nth function) were made very early when all Erlang work was done by the CS-lab. So they are definitely my fault, partly at least. And I still think it was a good decision. :-)

Binaries were added when Erlang was still at CS-lab, but there were no indexing operations as such. split_binary/2 ORIGINALLY had as argument the length of the first binary, but now it is the position of the split. It works the same either way. IIRC

The later BIFs binary_split/2/3 came longer after Erlang left the CS-lab and is in the hands of the OTP group and are explicitly zero indexed.

My view is that you could choose either depending on you how you swing, but choosing both is definitely the worst option. I am getting old and I value consistency. :-)

Robert

Reply all
Reply to author
Forward
0 new messages