valhalla-dev Digest, Vol 7, Issue 66

Thomas W twhitmore.nz at gmail.com
Mon Jan 19 23:26:05 UTC 2015


Hi Maurizio, Gavin, Brian,

Thanks for the clarification -- that was what I thought "meet rule" meant. And
no, what I had suggested (in terms of multi-dimensional matching on >=1
Tvars) was specifically formulated to *exclude* the need for meet rules. I
was discussing using a "precedence of matching" based L-to-R on
specificity, with a sole winning clause.

Multi-dimensional matching is not well-served by any loose or ambiguous
matching, which is why I had specified a system not needing such.

Rather than free-floating blocks which can specify any matching condition
on any TVar -- logically the equivalent of #ifdef blocks with arbitrary
conditions -- I proposed declaring a well-structured dimensionality around
the blocks to be matched. Contained clauses would exactly match this
dimensionality. A clear "most specific wins" rule, left-to-right, would
apply.

// implicit dimensionality:  could be anything, multiple blocks can appear
applicable
         __WhereRef(X) { /* ref X implementation */ }
         __WhereVal(Y) { /* val Y implementation */ }
         __WhereVal(X) __WhereRef(X) { /* val x, ref Y implementation */ }

// explicit format -- incomplete spatial coverage of (X, Y) will not
compile!
specializing on (X, Y)
    where (X is ref, Y is any)
    where (X is any, Y is val)
    where (X is val, Y is ref)

// explicit & correct -- would compile.
specializing on (X, Y)
    where (X is ref, Y is any)
    where (X is val, Y is any)
    where (X is val, Y is ref)

This identifies the error in Maurizio's ugly example very clearly -- by
producing a compile-time error, as (all possible X, all possible Y) are not
defined. [In the error example above, (X is val, *) would not be not fully
covered.]

The corollary, with such a scheme, is a requirement to check at
compile-time for "complete coverage" of the output space. ie, that a method
body could be produced for any possible (X, Y) specialization requested.

Some interesting points from that:
1)  "complete coverage" is most easily met by writing clauses which apply
to (any, any..) etc for all Tvars;
- these requires only one clause to fill the space & correctly fulfill the
method;  effectively a default.
2)  the alternative of (ref | val) bifurcation, requires 2^number(Tvar)
blocks to correctly fulfill a method.
- (ref, ref)
- (ref, val)
- (val, ref)
- (val, val)  etc. Longwinded & prone to error, and that's for one method
with only 2 Tvars.
3)  just on numerical order, there appears to be a strong preference to
code for 'any' as a default & specialize occasionally/ as needed.

I've been pushing the desirability of coding for 'any' generally, and the
desirability of generic Eq/ HashCode support specifically. But it appears
that exploration of "specialized blocks" also would support that preference.

> We think this should be an hard compile-time error - in fact, the current
compiler already gives you two kinds of errors when writing peeled methods:
> * errors for ambiguous specializations (i.e. where two or more
blocks match a given type-parameterization)
> * errors for missing specializations (i.e. if a given
parameterization has _no_ matching where blocks).

Great, I strongly agree with this. There should be as little as possible
(or no) ambiguity about what code a method is going to run. I guess what
I'm floating, is just a more formal declaration of which typevars a given
method/ section of code is going to be specialized on -- and a strict &
clear rule to select only one of these as applicable.

Overall, I still feel I prefer "coding for ANY" as a language paradigm.
Thanks for your feedback,


Regards,
Thomas


More information about the valhalla-dev mailing list