Search code examples
rascal

Type issues when trying to implement the Result type and partial() in Rascal


Type issues when trying to implement Result/Maybe and partial() in Rascal

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.

  1. First the IDE detect type issues at (*) even though the execution results in the expected value and type.
  2. Second when executing the code (**) the value is as expected even tough the type is not what I expect.
    • Resulting type is : Result[&S,&E] : Ok(Banana("banana"))
    • I expected the type: Result[Banana, Undefined]: Ok(Banana("banana"))

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.


Solution

  • Excellent question.

    • The type checker is still in beta, and may produce spurious errors and warnings. The interpreter will ignore them for now.
    • I suspect the run-time type is indeed Result[Banana, Undefined], you migh check this using the typeOf function in the Type library module. println("<typeOf(x)>")
    • If that's true, this {c,w}ould be a type instantiation bug (forgot to instantiate one level of type parameters in case of a higher-order function???) in the abstract interpretetation which produces the (static) types of variables, next to the interpreted results. Please submit this as an issue?
    • That (hypothetical) bug could also explain the run-time failure for the second implementation, which checks for sub-type on an accidentally non-instantiated type parameters and fails on this.