scala - 为隐式 Monad 使用 Scala 的定界延续

标签 scala monads continuations

我正在玩某种由单子(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 没有更高级别的多态性,尽管您可以通过一些扭曲来模拟它(参见 herehere )。好消息是,这里不需要那种火力。试试这些:

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/

相关文章:

Haskell 编译 IO-Action 顺序和刷新

http - 在服务器上使用无状态的 Spring Web Flow

mongodb - 无法将数组从 MongoDB 传递到需要向量的 Spark 机器学习函数

Scala lazy val 解释

scala - 与 Option.isEmpty 和 Option.get 相比,使用 Option.map 有什么优势?

haskell - 如何将图像转换为颜色矩阵?

haskell - 使用 bang 模式时 IO monad 会变得严格吗?

scala - 是否有用于包装执行 block 的类似延续的类型,例如 (Ctx => R) => R 或 (=> R) => R?

haskell - curry 语言的 CPS

Scala 数字类型模式匹配