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