Below you will find my attempt to implement the Result type including the functions Bind and Pure(aka return). All seems well until I put the parts together.
On the other hand when I use the second bind implementation (bindR2) the test (***) fails with an exception in the call: partial(bindR2, functionA)(PureR(Apple("Apple"))). Here the error is:
project:///src/Result.rsc|(964,10,<39,80>,<39,90>): Expected Result[Apple,&E], but got Result[Banana,Undefined]
Which shows that the resulting value and type I expected but Rascal expected a different type.
I was wondering if I'm somehow using the type-variable incorrectly in this code or of I'm not using the type-system correctly?
module Result
import Exception;
public alias Undefined = RuntimeException;
data Result[&S, &E] = Ok(&S) | Error (&E);
// PureR (aka return)
public Result[&S, &E] PureR(&S s) = Ok(s);
// bindR. first implementation of bind,
public Result[&S, &E] bindR( ( Result[&S, &E] ( &Sin ) ) f, Result[&Sin, &E] xR) {
switch (xR) {
case Ok(x) : return f(x);
case Error(e) : return Error(e);
}
}
// bindR2. Second implementation of bind,
public Result[&S, &E] bindR2( ( Result[&S, &E] ( &Sin ) ) f, Ok(x) ) = f(x);
public Result[&S, &E] bindR2( ( Result[&S, &E] ( &Sin ) ) f, Error(e) ) = Error(e);
// Tests BindR
// test helpers
public data Apple = Apple(str);
public data Banana = Banana(str);
public int add(int x, int y) = x + y;
public Result[num, void] fb(&Success <: num x) = Ok(x*3);
public Result[Banana, Undefined] functionA (Apple apple) = Ok(Banana("banana"));
test bool testBindResult1() = bindR(fb, Ok(3)) == Ok(9);
test bool testBindResult2() = bindR2(fb, Ok(3)) == Ok(9);
test bool testBindResult3() = bindR(fb, Error([])) == Error([]);
test bool testBindResult4() = bindR2(fb, Error([])) == Error([]);
test bool testBindResult5() = bindR(functionA, PureR(Apple("apple"))) == Ok(Banana("banana"));
test bool testBindResult6() = bindR2(functionA, PureR(Apple("apple"))) == Ok(Banana("banana")); // (*) <-- Type checker warning
// partial application.
public (&T (&X2)) partial( (&T (&X1, &X2)) f, &X1 x1 ) = (&T)( &X2 x2 ) { return f(x1, x2); };
// Test partial on BindR and BindR2
// Test
test bool testPartialAdd() = partial(add, 3)(3) == 6;
test bool testPartialBindR() = partial(bindR, functionA)(PureR(Apple("Apple"))) == Ok(Banana("banana")); // (**) <-- Type checker warning
// ERROR: test bool testPartialBindR2() = partial(bindR2, functionA)(PureR(Apple("Apple"))) == Ok(Banana("banana")); // (***) <-- Runtime type error.
When I lineup the parts of the error shown by the typechecker (red squiggly line at ** under partial(bindR, functionA)
) it shows the following:
Function of type
1. ...fun
2. ...... fun &T <: value ( &X2 <: value )
3. ...........( fun &T <: value ( &X1 <: value, &X2 <: value ), &X1 <: value )
cannot be called with argument types
4. .............fun Result[&S <: value, &E <: value] ( fun Result[&S <: value, &E <: value] (&Sin <: value), Result[&Sin <: value, &E <: value]), fun Result[Banana, RuntimeException](Apple)
2 is the partial function which evaluates to a function which takes a value of &X2
en produces a value of type &T
.
3 is the expected type of the input.
4 is the type of the provided input.
And all the types seem to lineup
&T <: value
lines up with Result[&S <: value, &E <: value]
&X1 <: value
lines up with fun Result[&S <: value, &E <: value] (&Sin <: value)
&X2 <: value
lines up with Result[&Sin <: value, &E <: value]
&X1 <: value
lines up with fun Result[Banana, RuntimeException](Apple)
This also shows that &X1 is mentioned twice. Once with type variables en once with the types of the provided function in the code.
In most code I see the typesystem is able to resolve the type of the type variable, in this case &S, &E and &Sin. &S should resolve to Banana, &E to RuntimeException and &Sin to Apple.
Excellent question.
Result[Banana, Undefined]
, you migh check this using the typeOf
function in the Type library module. println("<typeOf(x)>")