11

我正在玩某种由单子接口定义的 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 使用runOption函数运行(参见测试函数)。在runOption中调用的函数中调用的函数可以使用get函数或value方法从Option中获取值。如果值为None,Monad 将立即停止并返回None。因此不再需要对Option类型的值进行模式匹配。

问题是,我必须在runOption中使用“Any”类型并在get中使用延续的类型。

是否可以在 scala中表达runOption并使用 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)
}

谢谢!

4

1 回答 1

5

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 }

第二次尝试

好的,让我们再试一次,根据您在块中使用多个类型的示例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,并且在任何给定的块中,每次使用--see都runOption将始终是相同的类型参数,看看这会变得非常可怕。另一方面,您不再需要为块提供显式类型参数,但相比之下这是一个很小的胜利。所以这现在是完全类型安全的,但这不是我所说的用户友好,我猜用户友好是这个库的重点。valuetest_fnrunOption

我仍然坚信 rank-n 类型在这里不适用。如您所见,这里的问题现在是类型重构之一,而 rank-n 类型使重构更加困难,而不是更少!

于 2010-09-28T03:48:58.213 回答