11

私は、モナディックインターフェイスによって定義されたある種のDSLで遊んでいます。

一連のflatMapアプリケーションを使用してモナドを適用するのはちょっと面倒で、構文的にはそれほど美しくないので、区切られた継続を使用してモナドコードと非モナドコードを暗黙的に混合しようとしています。

実際には正常に機能していますが、コンパイル可能にするために「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

そしてscalaREPLでテストします:

scala> import BO._
scala> test4
res0: Option[Int] = Some(14)
scala> test5
res1: Option[Int] = None

Option-Monadは、runOption関数を使用して実行されます(テスト関数を参照)。runOption内で呼び出される関数は、 get関数またはvalueメソッドを使用して、 Optionから値を取得できます。値がNoneの場合、モナドはすぐに停止し、Noneを返します。したがって、 Option型の値でパターンマッチングを行う必要はありません。

問題は、runOptionで「Any」タイプを使用し、 getで継続のタイプを使用する必要があることです。

runOptionを表現して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)
}

ありがとう!

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 }

2 回目の試行

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

ここではランク n 型は適用できないと確信しています。ご覧のとおり、ここでの問題は型の再構築の問題であり、ランク n の型は再構築をより難しくします。

于 2010-09-28T03:48:58.213 に答える