Is there some way that I can have two types, say G and P where G is a subtype of P and both have operator overloading?
Here is what I’ve tried that almost works.
I have two interfaces: GuardedProcessI extends ProcessI. I’m happy with them except that I would like to use operator overloading. So I built GuardedProcess as an abstract type on top of GuardedProcessI and Process on top of ProcessI. There are from and to conversions for both, so most of the time I can ignore the interfaces and just use the abstract types. (Or perhaps the other way around.)
The problem is that that GuardedProcess is not a subtype of Process.
Here is a small example.
package tbc;
interface ProcessI {
public function bind( f : Int -> ProcessI ) : ProcessI ;
}
abstract Process( ProcessI ) to ProcessI from ProcessI {
public inline function new(p : ProcessI) {
this = p;
}
/** See ProcessI.bind */
@:op( A >= B )
public inline function bind( f : Int -> ProcessI ) : Process {
return new Process( (this:ProcessI).bind( f ) ) ;
}
}
interface GuardedProcessI extends ProcessI {
public function bind( f : Int -> ProcessI ) : GuardedProcessI ;
}
abstract GuardedProcess( GuardedProcessI )
to GuardedProcessI from GuardedProcessI
{
public inline function new(gp : GuardedProcessI) {
this = gp;
}
/** See GuardedProcessI.bind */
@:op( A >= B )
public inline function bind( f : Int -> ProcessI ) : GuardedProcess {
return new GuardedProcess( (this:GuardedProcessI).bind( f ) ) ;
}
}
@:expose class MFE {
public static function one( x : Process, f : Int -> Process ) : Process {
return x.bind( f ).bind(f) ;
}
public static function two( x : Process, f : Int -> Process ) : Process {
return (x >= f) >= f ;
}
public static function twoPoint5( x : Process, f : Int -> Process ) : Process {
return x.bind( f ) >= f ;
}
public static function three( x : GuardedProcess, f : Int -> Process ) : GuardedProcess {
return x.bind( f ).bind(f) ;
}
public static function four( x : GuardedProcess, f : Int -> Process ) : GuardedProcess {
return x.bind( f ) >= f ;
}
public static function five( x : GuardedProcess, f : Int -> Process ) : Process {
return one( x, f) ; // Error: tbc.GuardedProcess should be tbc.Process For function argument 'x'
}
public static function six( x : GuardedProcess, f : Int -> Process ) : Process {
return two( x, f) ; // Error: tbc.GuardedProcess should be tbc.Process For function argument 'x'
}
public static function seven( x : ProcessI, f : Int -> ProcessI ) : ProcessI {
return (x >= f) >= f ; // Cannot compare tbc.ProcessI and Int -> tbc.ProcessI
}
public static function eight( x : GuardedProcessI, f : Int -> ProcessI ) : ProcessI {
return seven( x, f ) ;
}
}
Everything compiles fine with version 4.0.5 except for functions five, six, and seven.
- I can fix function five by changing the parameter type of function one to ProcessI or by changing the call to
one( (x:ProcessI), f)
or toone( (x:GuardedProcessI), f)
, but I don’t want to do either; I really just want the client code (like the MFE class) to only have to deal with two types. - I can fix function six by rewriting the call to
two( (x:ProcessI), f)
or totwo( (x:GuardedProcessI), f)
, but again this means that the client coder needs to know about both the abstract types and interface types. - Functions seven and eight show what happens if I only use the interface types in the client code. The >=s in seven aren’t recognized as overloads because there is no implicit conversion from ProcessI to Process. I can put the conversion in by changing (x >= f) to (new Process(x) >= f). Once again this requires the client coder to know about both the interfaces and the abstracts.