There is a problem with operators like +, - and * which are both
infix and prefix. When you write
a - b
the parser knows you mean infix. But what about:
- (a,b)
Is this the prefix operator applied to pair, or is it the infix
operator?
Normally you wouldn't define an single argument operator on a tuple,
so if you write:
fun - (a:int) => .. // negative of a
fun - (a:int, b:int) => .. // subtraction
it's all clear. We actually have two functions here named "-"
which overload on a single argument, of types:
int
and
int * int
So everything works .. until you get to type classes.
Then it all falls apart. Consider:
class X[T] {
fun - (a:T) => ..
fun - (a:T,b:T) => ...
}
open[K] X[K];
var x = - (1,1); // WOOPS!
The open statement makes
fun -[T] : T -> ... // T = int * int
fun -[T] : T * T .. // T = int
visible and so the call is ambiguous. Even if you write:
- of (int * int) (1,1)
it doesn't help because both specialisations lead to that signature.
This problem cannot happen in a type class itself, because T inside
the class is not a type variable: it is existentially quantified not
universally quantified internally, which means it is just an unknown
abstract type, not a variable.
That's the difference between classes and modules.
In particular:
class X {
fun -[T] (a:T) => ..
fun -[T] (a:T,b:T) => ..
is NOT the same as the original class X I showed. This class is
a module, it isn't polymorphic, the individual functions are
universally quantified, that is, they're polymorphic.
In the former case the whole class is universally quantified
and polymorphic but the individual functions are monomorphic.
For them T is just a single unknown type, the same T everywhere.
However when you do:
open[K] X[K];
you're making all classes X with parameter K available, which means
all specialisations of the functions. Which ensures all calls to
operator - with a pair argument are ambiguous.
You may never have seen such an open statement .. but this
one is syntactic sugar for it, and is equivalent:
open X;
The moral of the story is: do NOT use the same operator name to define
a prefix and infix function.
Now, this does NOT mean we cannot write
- 1 and 1 - 1
and have it work. The parser knows which function it is.
But it generates
-1 and - (1,1)
at the moment.
In the old days, operator symbols couldn't be function names.
Instead we had
class X [T] {
fun neg (a:T) => ...
fun add (a:T,b:T) => ..
that is, the names were distinguished. The problem there was you had
to KNOW the mapping made by the parser. You had to know
prefix * mapped to a function named "deref".
So I allowed operator names as identifiers and changed the parser
and thus dug myself into a hole.
I could got back to "neg". Another solution is:
prefix - (a:int) => ....
infix - (a:int, b:int) =>
Another tricker solution:
fun (a:int) - (b:int) => ...
although I can't see how this fits with the grammar for lambdas or
C++ bindings.
It's almost possible to do this:
fun (a:int) - (b:int) / (c:int) => ...
i.e. provide a pattern. This is, after all, exactly what reduce does.
The only trick is that reductions aren't guaranteed to be provided.
Still .. it looks really cool!
--
john skaller
[email protected]
http://felix-lang.org
------------------------------------------------------------------------------
Want excitement?
Manually upgrade your production database.
When you want reliability, choose Perforce
Perforce version control. Predictably reliable.
http://pubads.g.doubleclick.net/gampad/clk?id=157508191&iu=/4140/ostg.clktrk
_______________________________________________
Felix-language mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/felix-language