Overriding built-in functions in a module
Richard O'Keefe
ok@REDACTED
Fri Jun 4 03:00:06 CEST 2010
There is currently a discussion going on in the SWI Prolog mailing
list about the way user code can break when a new operation is
added to the system. Alan Baljeu takes the reasonable position that
What's needed for stability ... is ...
Whatever I define within a module
is the definition used within [that] module.
The local [operations] supersede imported [ones].
Warn if something is shadowed, but allow it.
I was about to gloat about Erlang getting this right, and then I
thought I had better try it.
Consider the following module.
1 -module(foo).
2 -export([bar/0]).
3
4 bar() -> spawn(fun (X) -> X+1 end).
5
6 spawn(F) -> F(41).
where we're expecting foo:bar() to return 42.
erlc reports
/foo.erl:4: Warning: call to spawn/1 will call erlang:spawn/1; not
spawn/1 in this module
(add an explicit module name to the call to avoid this warning)
./foo.erl:6: Warning: defining BIF spawn/1
./foo.erl:6: Warning: function spawn/1 is unused
The second message is clearly WRONG: this _can't_ be defining the
built-in function erlang:spawn/1 because this isn't the erlang:
module. What's more, it clearly DIDN'T define erlang:spawn/1;
the last message complains that there *is* a function spawn/1 in
this module which is not used.
So let's do what the first error message says:
* 4 bar() -> ?MODULE:spawn(fun (X) -> X+1 end).
m% erlc foo.erl
./foo.erl:6: Warning: defining BIF spawn/1
./foo.erl:6: Warning: function spawn/1 is unused
There's an explicit call there, how can it not be used?
Change it again, so that now it reads
* 1 -module(foo).
2 -export([bar/0,spawn/1]).
3
* 4 bar() -> ?MODULE:spawn(fun (X) -> X+1 end).
5
6 spawn(F) -> F(41).
Erlc is now silent, and foo:bar() gives the expected answer.
The price is that a function which was never meant to be exported
(and because of the clash with a built in function never _should_
be exported, to avoid confusion) *has* to be exported, and a call
to a purely local function has to be a remote call.
This is silly.
I chose spawn/1 as an example of a function that *was* added as
a pervasive export from erlang: long after people had been using
Erlang, and rightly so. There's no reason to believe that this
is the last such function to be *rightly* added.
We need the following things:
(1) If a function f/n is defined in a module,
then any calls to f/n without a module prefix
refer to THAT definition.
(2) The warning message for a definition of such
a function should not say that it is defining
a BIF but that it is defining a function with
the same NAME as a BIF, e.g.,
./foo.erl:6: Warning: there is a BIF called spawn/1;
to use the BIF you will need an erlang: prefix.
(3) Such warnings are useful, but sometimes it is
intentional, so a directive such as
-private([spawn/1]).
would be useful to silence the warning while giving
human readers (and the compiler!) adequate notice.
(Amongst other things, in a code review, it is easier
to grep for -private than to gather compiler warnings.)
I'm sure I've seen a proposal like this before, so I haven't written
up an EEP for it yet.
More information about the erlang-questions
mailing list