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
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.
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().
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).
On 01/22/2013 07:34 AM, Björn Gustavsson wrote:
Why not use the existing -export() attribute and write like
It still allows Evan Miller to write this: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?
-attr([first_name/1, last_name/1, ...]).
$handle_undefined_function(F, [Model]) ->
{_, Value} = lists:keyfind(F, 1, Model),
Value.
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.
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.
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
> > >>> person.name <http://person.name> <http://person.name>().
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?
- 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
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).
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.
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
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)
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.
There are only two hard problems in computer science: cache invalidation, naming things, and off by one errors.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.