Учитывая канонический пример из исследовательской работы для продолжений Scala с разделителями, немного изменен, так что ввод функции в shift
имеет имя f
и, таким образом, больше не является анонимным.
def f(k: Int => Int): Int = k(k(k(7)))
reset(
shift(f) + 1 // replace from here down with `f(k)` and move to `k`
) * 2
Плагин Scala преобразует этот пример так, что вычисление (в пределах входного аргумента reset
), начиная с каждого shift
до вызова reset
, заменяется на функцию (например, f
) вход для shift
.
Замененное вычисление смещено (т.е. перемещено) в функцию k
. Функция f
вводит функцию k
, где k
содержит замененное вычисление, k
input x: Int
, а вычисление в k
заменяет shift(f)
на x
.
f(k) * 2
def k(x: Int): Int = x + 1
Который имеет тот же эффект, что и:
k(k(k(7))) * 2
def k(x: Int): Int = x + 1
Обратите внимание, что тип Int
входного параметра x
(то есть сигнатура типа k
) был задан сигнатурой типа входного параметра f
.
Еще один заимствованный пример с концептуально эквивалентной абстракцией, т. Е. read
- это функция, введенная в shift
:
def read(callback: Byte => Unit): Unit = myCallback = callback
reset {
val byte = "byte"
val byte1 = shift(read) // replace from here with `read(callback)` and move to `callback`
println(byte + "1 = " + byte1)
val byte2 = shift(read) // replace from here with `read(callback)` and move to `callback`
println(byte + "2 = " + byte2)
}
Полагаю, это будет переведено в логический эквивалент:
val byte = "byte"
read(callback)
def callback(x: Byte): Unit {
val byte1 = x
println(byte + "1 = " + byte1)
read(callback2)
def callback2(x: Byte): Unit {
val byte2 = x
println(byte + "2 = " + byte1)
}
}
Я надеюсь, что это объясняет общую связную абстракцию, которая была несколько омрачена предварительным представлением этих двух примеров. Например, первый канонический пример был представлен в исследовательской работе как анонимная функция вместо моего имени f
, поэтому некоторые читатели не сразу поняли, что он абстрактно аналогичен read
во заимствованном втором примере.
Продолжения, разделенные таким образом, создают иллюзию инверсии контроля от «ты звонишь мне извне reset
» до «я звоню тебе внутри reset
».
Обратите внимание, что тип возвращаемого значения f
есть, но k
нет, должен совпадать с типом возвращаемого значения reset
, т. Е. f
может свободно объявлять любой тип возвращаемого значения для k
пока f
возвращает тот же тип, что и reset
. То же самое для read
и capture
(см. Также ENV
ниже).
Продолжения с разделителями неявно не инвертируют контроль состояния, например, read
и callback
не являются чистыми функциями. Таким образом, вызывающая сторона не может создавать ссылочно-прозрачные выражения и поэтому не имеет декларативного (например, прозрачного) контроля над предполагаемой императивной семантикой .
Мы можем явно получить чистые функции с продолжением с разделителями.
def aread(env: ENV): Tuple2[Byte,ENV] {
def read(callback: Tuple2[Byte,ENV] => ENV): ENV = env.myCallback(callback)
shift(read)
}
def pure(val env: ENV): ENV {
reset {
val (byte1, env) = aread(env)
val env = env.println("byte1 = " + byte1)
val (byte2, env) = aread(env)
val env = env.println("byte2 = " + byte2)
}
}
Полагаю, это будет переведено в логический эквивалент:
def read(callback: Tuple2[Byte,ENV] => ENV, env: ENV): ENV =
env.myCallback(callback)
def pure(val env: ENV): ENV {
read(callback,env)
def callback(x: Tuple2[Byte,ENV]): ENV {
val (byte1, env) = x
val env = env.println("byte1 = " + byte1)
read(callback2,env)
def callback2(x: Tuple2[Byte,ENV]): ENV {
val (byte2, env) = x
val env = env.println("byte2 = " + byte2)
}
}
}
Становится шумно из-за явного окружения.
Заметим, что в Scala отсутствует глобальный вывод типов из Haskell, и поэтому, насколько я знаю, не удалось поддержать неявное поднятие до unit
монады состояний (как одной из возможных стратегий скрытия явной среды), потому что глобальный Haskell ( Вывод типа Хиндли-Милнера) зависит от , не поддерживающего множественное виртуальное наследование алмазов .