Question

I'm trying to build a coroutine framework to enable batch data fetching by stepping through each data-dependent function in parallel. Here is what I have so far: http://pastie.org/7147798

  1. This doesn't work

    def get(id: Long) = reset {
      // Is it not already cached?
      if (!cached.isDefinedAt(id)) {
        // Store the ID we want to fetch.
        queued += id
        // Come back later...
        shift { fetch[Object]() } : Seq[Any] @cps[ExecState[Object]]
      }
      // We should have the ID fetched now.
      Result(cached(id))
    }
    

    I get the following error

    ashoat@ashoatmbp [~/project]# scala -P:continuations:enable Loader.scala
    /Users/ashoat/project/Loader.scala:134: error: type mismatch;
     found   : Unit
     required: Any @util.continuations.package.cps[Main.$anon.Loader.ExecState[Main.$anon.Loader.Object]]
          if (!cached.isDefinedAt(id)) {
          ^
    one error found
    

    This works

    def get(id: Long) = reset {
      // Is it not already cached?
      if (!cached.isDefinedAt(id)) {
        // Store the ID we want to fetch.
        queued += id
        // Come back later...
        shift { fetch[Object]() } : Seq[Any] @cps[ExecState[Object]]
        // We should have the ID fetched now.
        Result(cached(id))
      } else {
        // We should have the ID fetched now.
        Result(cached(id))
      }
    }
    
  2. This doesn't work

    val getFive = reset {
      if (true) {
        Result(5)
      } else {
        val seq: Seq[Any] = shift { fetch[Int](Object.get(15181990251L)) }
        val Seq(obj: Object) = seq
        Result(obj.fields("test").toInt)
      }
    }
    

    I get the following error

    ashoat@ashoatmbp [~/project]# scala -P:continuations:enable Loader.scala
    /Users/ashoat/project/Loader.scala:170: error: cannot cps-transform expression new this.Loader.Result[Int](5): type arguments [this.Loader.Result[Int],this.Loader.Result[Int],Nothing] do not conform to method shiftUnit's type parameter bounds [A,B,C >: B]
        Result(5)// : Result[Int] @cps[Result[Int]]
              ^
    one error found
    

    This works

    val getFive = reset {
      if (true) {
        Result(5) : Result[Int] @cps[Result[Int]]
      } else {
        val seq: Seq[Any] = shift { fetch[Int](Object.get(15181990251L)) }
        val Seq(obj: Object) = seq
        Result(obj.fields("test").toInt)
      }
    }
    

    But I get the following warning

    ashoat@ashoatmbp [~/project]# scala -P:continuations:enable Loader.scala
    /Users/ashoat/project/Loader.scala:170: warning: expression (new this.Loader.Result[Int](5): this.Loader.Result[Int]) is cps-transformed unexpectedly
        Result(5) : Result[Int] @cps[Result[Int]]
                  ^
    one warning found
    8
    

No correct solution

OTHER TIPS

Although I still don't quite understand continuations myself, as best as I can tell, the key issue in your example is that your code does not always supply a shift to the reset.

The compiler expects to find some shift nested inside the reset. It will then CPS transform the shift into a ControlContext][A, B, C] and the code that happens after the shift into a ControlContext.map call.

Because you have an if statement, in the case where the else branch is taken, there is no nested shift:

reset {
  if (false) {
    shift { ... }
  } 
  Result(cached(id)) // no shift
}

Same with

reset {
  if (false) {
    shift { ... }
  } else {
    Result(cached(id)) // no shift
  }
}

That cannot be transformed into valid CPS code.

It seems you could have the reset inside the if branch or supply a trivial shift statement to the else branch:

if (!cached.isDefinedAt(id)) reset {
   shift { ... }
   Result(cached(id))
} else {
   Result(cached(id))
}

// or

reset {
  if (!cached.isDefinedAt(id)) {
    shift { ... }
    Result(cached(id))
  } else {
    shift[Result[Object], ExecState[Object], ExecState[Object]] { k => 
      Result(cached(id))
    }
  }
}    

Edit: It does seems there is some inconsistencies on how the cps plugin infers the types. For example:

var b = false
def test[A](a: A) = reset {
  if (b) {
    a
  } else {
    shift{ (k: Unit => A) => k() }
    a
  }
}

Running compilation with the -Xprint:selectivecps options shows that the compiler infers the type as Reset[A, Nothing] then running the code will produce an error at runtime. If the if is reversed as:

var b = false
def test[A](a: A) = reset {
  if (b) {
    shift{ (k: Unit => A) => k() }
    a
  } else {
    a
  }
}

Then the compiler correctly infers reset[A, A]. If I provide the type parameters to reset like test[A](a: A) = reset[A, A] { then it works in both cases.

Maybe specifying the type parameters to reset and shift and also instead of using Result(5), using the shiftUnit[A, B, C] method will help with reducing inconsistencies.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top