我正在使用由monadic接口定义的某种DSL。
由于使用一堆flatMap应用程序应用monad有点麻烦,我发现语法上的理解并不那么美,我试图使用分隔的延续来隐含地混合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))
}
使用以下代码编译代码: $ scalac -P:continuations:enable BO.scala
并在scala REPL中进行测试:
scala> import BO._
scala> test4
res0: Option[Int] = Some(14)
scala> test5
res1: Option[Int] = None
使用 runOption 功能运行Option-Monad(请参阅测试功能)。在 runOption 中调用的函数可以使用 get 函数或值方法从选项中获取值。如果值为无,Monad将立即停止并返回无。因此,不再需要对选项类型的值进行模式匹配。
问题是,我必须在 runOption 中使用“Any”类型,并在 get 中使用延续类型。
是否可以在scala中使用rank-n类型表达 runOption 和获取? 所以我可以写:
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)
}
谢谢!
答案 0 :(得分:5)
Scala没有更高级别的多态性,尽管您可以通过一些扭曲来模拟它(请参阅here和here)。好消息是,这里不需要那种火力。试试这些:
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 }
好的,让我们再试一次,根据您在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] }
}
不幸的是,正如您所看到的,结果不是很好。由于Scala的类型推理能力有限,您需要为value
的大多数用途提供显式类型参数,并且在任何给定的runOption
块中,它始终是相同的 value
的每个用法的类型参数 - 请参阅test_fn
,了解其中非常糟糕的地方。另一方面,您不再需要为runOption
块提供显式类型参数,但相比之下,这是一个非常小的胜利。所以现在这是完全类型安全的,但它不是我称之为用户友好的,我猜测用户友好性是这个库的重点。
我仍然相信rank-n类型在这里不适用。正如你所看到的,这里的问题现在是类型重建之一,而rank-n类型使重建更多变得困难,而不是更少!