Какие параметры типа я должен использовать, чтобы List.map работал с продолжениями с разделителями?
Я в основном следую примеру, приведенному на странице API Scala, для продолжения с разделителями. Код ниже работает нормально:
import scala.util.continuations._
import scala.collection.mutable.HashMap
val sessions = new HashMap[Int, Int=>Unit]
def ask(prompt: String): Int @cps[Unit] = shift {
ret: (Int => Unit) => {
val id = sessions.size
printf("%s\nrespond with: submit(0x%x, ...)\n", prompt, id)
sessions += id -> ret
}
}
def submit(id: Int, addend: Int): Unit = {
sessions.get(id) match {
case Some(continueWith) => continueWith(addend)
}
}
def go = reset {
println("Welcome!")
val first = ask("Please give me a number")
val second = ask("Please enter another number")
printf("The sum of your numbers is: %d\n", first + second)
}
Тем не менее, когда я изменяю go
к следующему:
def go = reset {
println("Welcome!")
List("First?","Second?").map[Int @cps[Unit]](ask)
}
Я получаю эту ошибку:
error: wrong number of type parameters for method map: [B, That](f: String => B)
(implicit bf: scala.collection.generic.CanBuildFrom[List[String],B,That])That
List("First?","Second?").map[Int @cps[Unit]](ask)
^
Добавление Any
как параметр второго типа не помогает. Любая идея, какие типы я должен поставлять?
3 ответа
Вот самая близкая вещь, которую я мог решить. Он использует shiftR для уточнения продолжения, а не для его сброса, использует foldRight для построения приостановленной цепочки продолжения, использует блок сдвига / сброса для получения продолжения после приостановки и "одушевленный" метод для запуска приостановленного продолжения.
import scala.collection.mutable.HashMap
import scala.util.continuations._
val sessions = new HashMap[Int, (Unit=>Unit, Int)]
val map = new HashMap[Int, Int]
def ask(pair:(String, Int)) = pair match {
case (prompt, index) => shiftR { (ret: Unit => Unit) => {
val id = sessions.size
printf("%s\nrespond with: submit(0x%x, ...)\n", prompt, id)
sessions += id -> (ret, index)
()
}}
}
def submit(id: Int, addend: Int): Unit = {
sessions.get(id) match {
case Some((continue, index)) => { map.put(index, addend); continue() }
}
}
def sum(m:HashMap[Int,Int]) : Int = {
m.fold[(Int, Int)]((0, 0))((a, b) => (0, {a._2+b._2}))._2
}
type Suspended = ControlContext[Unit,Unit,Unit]
class AnimateList(l:List[Suspended]) {
def suspend(k: => Unit) = (c: Unit) => k
def animate(k:Unit => Unit): Unit = {
l.foldRight(k)(
(elem: Suspended, acc: Unit => Unit) => suspend(elem.fun(acc, ex => ())))()
}
}
implicit def listToAnimateList(l:List[Suspended]) = new AnimateList(l)
reset {
val conts = List("First?","Second?","Third?").zipWithIndex.map(ask)
shift { conts.animate }
println(sum(map))
}
Причина в том, что это просто невозможно без создания CPS-трансформированного map
метод на List
: аннотации CPS заставляют компилятор перевернуть ваши методы "наизнанку", чтобы передать продолжение туда, где это необходимо и стандарт List.map
не подчиняется преобразованному договору. Если вы хотите какое-то время погрузить свой разум в бутылки Кляйна, вы можете посмотреть файлы классов, созданные из вашего источника, в частности сигнатуры методов.
Это основная причина, по которой плагин CPS никогда не будет полным общим решением этой проблемы, что не связано с недостатком, а вызвано внутренним несоответствием между "прямым" кодом и стилем передачи продолжения.
Вам нужно дать правильный параметр для неявного CanBuildFrom, который будет найден:
List("First?","Second?").map[Int @cps[Unit], List[Int @cps[Unit]](ask)
Но действительно ли вам нужно четко указывать тип? может просто сделать .map(ask)
буду работать.