Proposed change to libraries

Richard A. O'Keefe ok@REDACTED
Wed Feb 9 02:52:02 CET 2005

I proposed
 > >   is_applicable(F, N)
 > >     - succeeds or returns true when N is a non-negative integer
 > >       and F is such that F(T1,...,TN) would make sense
 > >     - fails or returns false otherwise
 > >   is_applicable(F)
 > >     - succeeds or returns true when there exists an N such that
 > >       is_applicable(F, N) would succeed or return true
 > >     - fails or returns false otherwise

The words "would make sense" were very carefully chosen.

Kostis Sagonas <kostis@REDACTED> wrote:
	However, apparently I am missing the obvious, because I do not see
	how this is easier (though I see that it is more elegant) to
	implement than what we were essentially discussing yesterday...
It isn't easier to IMPLEMENT but easier to USE.

Remember the context.  Someone else proposed

    map(F, [H|T]) -> [F(H) | map(F, T)];
    map(F, []) when is_function(F) -> [];
    map({M,F}, []) when is_atom(M), is_atom(F) -> [].

This is problematic in two ways.
First, when the argument *is* a function it doesn't check as much as
it easily could.  (It doesn't check the arity.)

Second, the extra clause to check for {M,F} is such a pain to add
that it probably won't be done consistently throughout the libraries.

is_applicable/[1,2] is meant to address both points:
 - if there is a required arity, that can be passed
   and used for a more precise test
 - it is *MUCH* easier to use than adding an extra clause.

	The key question is: does "is_applicable({M,F}, 1)" always check that
	the M:F/1 is a valid fun (and succeeds accordingly) or not?
"would make sense" is not the same as "is currently defined".
Like other guards, is_applicable/[1,2] is meant to be a *time-independent*
test which can be checked by *local inspection* of the term in question.
Consider this example:

    m:f(X) -> unload module X.

    lists:map({m,f}, [m])

At the time the call *starts*, m:f/1 *is* defined.
By the time the call reaches [], m:f/1 is *not* defined any longer.
If we had the stronger time-dependent non-local check we'd get a
run-time error, for a call that actually made sense at the beginning
and has in fact completed without any difficulty.

Closures don't have that problem.  Once a closure of arity 1, always
a closure of arity 1.  That doesn't actually mean they are safer:

    lists:map(fun (X) -> m:f(X) end, [m])

It makes sense to think of {m,f} as short-hand for
"whichever of
    fun (X1) -> m:f(X1) end
    fun (X1,...,X99) -> m:f(X1,...,X999) end
 is actually required at the time of call."
So it makes sense to treat it has having the same validity as the
corresponding fun.

	(I am assuming the "2" above is just a typo and should read "1" BTW).
No, it was a thinko:  too much Prolog.  It should read "1".

More information about the erlang-questions mailing list