[eeps] Multi-Parameter Typechecking BIFs

Richard O'Keefe ok@REDACTED
Tue Feb 24 01:10:34 CET 2009


On 23 Feb 2009, at 10:59 pm, mats cronqvist wrote:
>> "Oops there is a problem here,
>> so let us imagine that it has been solved."
>
>  you're a very weird guy. (that's a compliment.)

My daughters say the same thing.
I try.
Ying tong iddle I po!
For a good time, visit
http://www.thegoonshow.net/downloads/other/what_time_is_it_eccles.mp3

>  Separating the pattern match from the condition is bad. The 'when'
>  keyword should have never been introduced.

There are conditions that pertain to *one* thing.
It may make sense to put those in the pattern, though I doubt it.
There are conditions that relate *more than one* thing.
It is an essential property of such conditions that there is
no one place in the pattern where they belong.

Take the example

	max(X, Y) when X >= Y -> X;
	max(X, Y) when X =< Y -> Y.

(Yes, you do see the influence of Dijkstra in the way that's
written.)  Which argument would you attach "X > Y" to?

Why don't I see people arguing in the Haskell mailing list
that the inclusion of guards in Haskell was a mistake?

>>>> Where does it say {M,F,A}::names_a_function?
>
>  Mmmm... nowhere?

BUT IT ***SHOULD***!

>>>> This is a very very low level way of describing what you want.
>
>  If you say so.

This is not a matter of opinion.
The Erlang type tests are as low level as Erlang lets you get.

>  Meh. This discussion is about
> f(..., {D::integer,M::integer,Y::integer}, ...) -> ...
>
>  vs.
> f(..., {D,M,Y}, ...)
> when is_integer(D),is_integer(M),s_integer(Y) -> ...

No it isn't.  It is about whether {D::integer,M::integer,Y::integer}
is a tolerable notation.  Any demand that the only alternative to be
considered is the most verbose and lowest level way to express our
intent in Erlang is, well, not in the best interests of Erlang or its
users.

We *don't* just happen to want some variables to be integers
or some variables to be floats or some variables to be atoms &c.
We want them to have certain types *for a reason*.

In the case of Wings3D, the important point is not "X and Y and Z
just happen to be floats in this particular place" but "{X,Y,Z} is
an instance of the representation of 3d vectors I am using throughout
this application".  In the case of {M,F,A}, the important point is
not "M and F happen to be atoms and A happens to be a non-negative
integer in this particular place for obscure and non-generalisable
reasons" but "{M,F,A} is an instance of the representation of
function names used throughout Erlang".

If someone were to propose that
	{D,M,Y}::date
or	{M,F,A}::func_name
or	{X,Y,Z}::vector3
should be expressible, that would be different.

But that's not actually what we are discussing.

What we are discussing is a proposal to allow
	{D,M,Y} .... when is_integer(D,M,Y)
	{M,F,A} .... when is_atom(M,F), is_integer(A)
	{X,Y,Z} .... when is_float(X,Y,Z)
The subject _is_ "Multi-parameter typechecking BIFs" after all.

What does that proposal buy us?
The ability to say a tiny amount of typing when
more than one variable coincidentally happens to
need the same type test.

That's it.

The proposal we're supposed to be discussing does not
distinguish between accidental and essential commonality of
representation, remains at the lowest possible semantic
level, and doesn't even save any typing compared with a
disciplined use of macros.

Compare

	{D,M,Y} .... when is_integer(D, M, Y)
	{D,M,Y} .... when ?is_date(D, M, Y)

	{M,F,A} .... when is_atom(M, F), is_integer(A)
	{M,F,A} .... when ?is_func_name(M, F, A)

	{X,Y,Z} .... when is_float(X, Y, Z)
	{X,Y,Z} .... when ?is_vec3(X, Y, Z)

The proposal to put Erlang type tests inside guards
has all the defects of the multiparameter typechecking BIF
proposal plus one more:  it requires the type tests to be
written repeatedly, which is precisely the problem that
the original poster was trying to solve.

>  Macros. Wonderful. Actually, there is a way to declare types;
> -type(date() :: {integer(),integer(),integer()}).

Indeed there is.  But you cannot use them in guard tests
or patterns.  (That particular type declaration fails to
specify *ranges* on the elements, and Erlang types still
cannot express constraints such as {X,Y} ... where Y divides X.)

>>> But then I have to go looking in a header file somewhere to find the
>>> -define(mod_func_arity,...).
>>> Yuck.
>>
>> Yes you do.  But at least it is there to be found.
>> (Most modern text editors support some kind of tags facility,
>
>  but not syntax haghlighting?

Did you read the next bit where I pointed out that I use an
editor that *doesn't* support tags, and even there I don't
regard looking things up as a great burden?
>
>
>  That's fascinating.

That's a curious response to the demolition of your argument.

>  because real code (here defined as written by professional
>  programmers (i.e. people that program from 9 to 5) as opposed to CS
>  professors) looks like this;
>
> bla(FooBlaBaxBar, FooFooBlaBaxBar, LaDiDaBaxBar, FooFooBlaBazBar,
>    BlaBlaaBarBar, FooFooBlaBurpBar) ->
>  when is_integer(FooFooBlaBaxBar), is_atom(FooFooBlaBazBar) ->
>

Hmm.  Code that looked like that would not get through syntax
checking.  But since you are making a claim about "real code",
why not *display* some real code, and let's see what we can do
with it.

Let me offer the following recommendation to Real Programmers:
unless and until something better (such as abstract patterns,
or something that integrates -type declarations with guards)
is provided, avoid "bare" type tests in favour of your own
type test macros whenever it makes sense to do so.  By doing
that, you will be saving yourself work and making your code
more readable.


>> Oddly enough, I would regard type tests in patterns as creating a
>> barrier to type checking that would make people skip it altogether.
>
>   That is indeed very odd. Almost weird.

Not half as weird as a belief that types in patterns are a good idea.
It's the simple fact that types in patterns make patterns *bigger*,
and therefore harder to read.





More information about the eeps mailing list