urs*_*rso 11 monads continuations scala
我正在玩一种由monadic接口定义的DSL.
因为使用一堆flatMap应用程序应用monad是一种麻烦,我发现语法上的理解并不那么美,我试图使用分隔的continuation隐含地混合monadic和non monadic代码.
它实际上工作得很好,但我真的不满意这些类型,因为我必须将我的自我限制为"任意"类型才能编译:(.因此,当结果使用时,使用"Any"和"cast"需要可能导致运行时错误......
下面是一些示例代码,用于将Scala中的Option-Monad与常规代码混合,因此您可以看到我在说什么:
object BO {
import scala.util.continuations._
def runOption[C](ctx: => Any @cpsParam[Option[Any],Option[Any]]): Option[C] = {
val tmp : Option[Any] = reset {
val x : Any = ctx
Some(x)
}
tmp.asInstanceOf[Option[C]]
}
def get[A](value:Option[A]) = shift { k:(A=>Option[Any]) =>
value.flatMap(k)
}
class CPSOption[A](o:Option[A]) {
def value = get[A](o)
}
implicit def opt2cpsopt[A](o:Option[A]) = new CPSOption(o)
def test1 = runOption[Int] {
val x = get(None)
x
}
def test2 = runOption[Int] {
val x = Some(1).value
x
}
def test3 = runOption[Int] {
val x = Some(1)
val y = Some(2)
x.value + y.value
}
def test_fn(x:Option[Int], y:Option[Int], z:Option[Int]) = runOption[Int] {
x.value * x.value + y.value * y.value + z.value * z.value
}
def test4 = test_fn(Some(1), Some(2), Some(3))
def test5 = test_fn(Some(1), None, Some(3))
}
Run Code Online (Sandbox Code Playgroud)
编译代码:$ scalac -P:continuations:enable BO.scala
并在scala中测试REPL:
scala> import BO._
scala> test4
res0: Option[Int] = Some(14)
scala> test5
res1: Option[Int] = None
Run Code Online (Sandbox Code Playgroud)
Option-Monad使用runOption函数运行(请参阅测试函数).在runOption中调用的函数可以使用get函数或value方法从Option中获取值.如果值为None,Monad将立即停止并返回None.因此,不再需要对Option类型的值进行模式匹配.
问题是,我必须在runOption中使用"Any" 类型,并在get中使用continuation的类型.
是否有可能以表达runOption并获得在斯卡拉与秩n种?所以我可以写:
def runOption[C](ctx: forall A . => A @cpsParam[Option[A], Option[C]]) : Option[C] =
...
def get[A](value:Option[A]) = shift { k:(forall B . A=>Option[B]) =>
value.flatMap(k)
}
Run Code Online (Sandbox Code Playgroud)
谢谢!
Scala没有更高级别的多态性,尽管你可以用一些扭曲来模拟它(见这里和这里).好消息是,这里不需要那种火力.试试这些:
def runOption[A](ctx: => A @cps[Option[A]]): Option[A] = reset(Some(ctx))
def get[A](value:Option[A]) = shift { k:(A=>Option[A]) => value flatMap k }
Run Code Online (Sandbox Code Playgroud)
好吧,让我们再试一次,根据您在runOption块中使用多个类型的示例:
object BO {
import scala.util.continuations._
def runOption[A](ctx: => A @cps[Option[A]]): Option[A] = reset(Some(ctx))
def get[A, B](value:Option[A]):A @cps[Option[B]] = shift { k:(A=>Option[B]) =>
value flatMap k
}
class CPSOption[A](o:Option[A]) {
def value[B] = get[A, B](o)
}
implicit def opt2cpsopt[A](o:Option[A]) = new CPSOption[A](o)
def test1 = runOption {
val x = get[Int, Int](None)
x
}
def test2 = runOption {
Some(1).value[Int]
}
def test3 = runOption {
val x = Some(1)
val y = Some(2)
x.value[Int] + y.value[Int]
}
def test_fn(x:Option[Int], y:Option[Int], z:Option[Int]) =
runOption (x.value[Int] * x.value[Int] +
y.value[Int] * y.value[Int] +
z.value[Int] * z.value[Int])
def test4 = test_fn(Some(1), Some(2), Some(3))
def test5 = test_fn(Some(1), None, Some(3))
def test6 = runOption { val x = Some(1)
val y = Some(2)
x.value[Boolean] == y.value[Boolean] }
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,正如你所看到的,结果不是很好.由于Scala的有限类型推理能力,您需要为大多数用途提供显式类型参数value,并且在任何给定的runOption块中,对于每次使用它都将始终是相同的类型参数- 请查看它变得非常可怕的地方.另一方面,您不再需要为块提供显式类型参数,但相比之下,这是一个非常小的胜利.所以现在这是完全类型安全的,但它不是我称之为用户友好的,我猜测用户友好性是这个库的重点.valuetest_fnrunOption
我仍然相信rank-n类型在这里不适用.正如你所看到的,这里的问题现在是类型重建之一,而rank-n类型使重建变得更加困难,而不是更少!