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