Использование продолжения Scala с разделителями для неявных монад - PullRequest
11 голосов
/ 02 сентября 2010

Я играю с каким-то DSL, определяемым монадическим интерфейсом.

Поскольку применение монады с использованием нескольких приложений flatMap довольно обременительно, и синтаксически синтаксически я не понимаю, чтоЯ пытаюсь неявно смешивать монадический и не монадический код, используя продолжения с разделителями.

Это на самом деле работает нормально, но я действительно не доволен типами, потому что я должен ограничивать себя типом "Любой"сделать при компиляции :(. Таким образом, использование «Any» и «casting» позже, когда потребуется результат, может привести к ошибкам во время выполнения ...

Вот пример кода для смешивания Option-Monad в Scala собычный код, чтобы вы могли видеть, о чем я говорю:

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: продолжений: включите 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 function или метод value для получения значения из Option .Если значение равно Нет , монада немедленно остановится и вернет Нет .Так что больше нет необходимости в сопоставлении с образцом для значения типа Option .

Проблема в том, что мне нужно использовать тип "Any" в runOption идля типа продолжения в get .

Можно ли выразить runOption и get с типами ранга n в scala?Так что я могу написать:

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)
}

Спасибо!

1 Ответ

5 голосов
/ 28 сентября 2010

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, и в любом данном блоке runOption это всегда будет тот же самый параметр типа для каждое использование value - см. test_fn, где это становится довольно ужасно. С другой стороны, вам больше не нужно указывать явный параметр типа для блока runOption, но это сравнительно небольшой выигрыш в сравнении. Так что теперь это абсолютно безопасно для типов, но это не то, что я бы назвал удобным для пользователя, и я предполагаю, что удобство было целью этой библиотеки.

Я по-прежнему убежден, что типы ранга n здесь не применимы. Как видите, проблема здесь заключается в реконструкции типов, и типы ранга n делают восстановление больше сложным, а не меньше!

...