我正在玩某种由单子(monad)接口(interface)定义的 DSL。
由于使用一堆 flatMap 应用程序应用 monad 有点麻烦,而且我发现在语法上理解起来并不那么漂亮,所以我试图使用定界延续来隐式地混合 monadic 和非 monadic 代码。
它实际上工作正常,但我对这些类型真的不满意,因为我必须将自己限制在可编译的类型“Any”:(。因此,稍后在结果是时使用“Any”和“casting”需要可能会导致运行时错误...
这是一些将 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
Option-Monad 使用 运行。运行选项 功能(见测试功能)。在 内部调用的函数运行选项 可以使用获取 函数或 值(value) 从 获取值的方法选项 .如果值为 无 , Monad 将立即停止并返回 无 .因此不再需要对 类型的值进行模式匹配选项 .
问题是,我必须在 中使用类型“Any”。运行选项 对于 中的延续类型获取 .
是否可以表达运行选项 和 获取 在scala中使用rank-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)
}
谢谢!
最佳答案
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
block ,对于 value
的每次使用,它始终是相同的类型参数--见test_fn
因为这变得非常可怕。另一方面,您不再需要为 runOption
提供显式类型参数。 block ,但相比之下这是一个很小的胜利。所以这现在是完全类型安全的,但这不是我所说的用户友好,我猜用户友好是这个库的重点。我仍然坚信 rank-n 类型在这里不适用。如您所见,这里的问题现在是类型重构之一,而 rank-n 类型使重构更加困难,而不是更少!
关于scala - 为隐式 Monad 使用 Scala 的定界延续,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3622944/