Решил попробовать написать некоторую вариацию restarts
Для Ъ: перезапуски Lisp по-простому - более общая концепция exceptions, при которой стек не раскручивается, то-есть обработка исключительной ситуации происходит в месте возникновения, а не выше по стеку полностью прервав исполнение. То-есть концептуально алгоритм все еще может продолжить работу если код который его вызывал предложил способы разрулить нестандартные ситуации по мере выполнения. Отличие от лямбд переданных во внутрь алгоритма заключается просто в том, что они явно не передаются, а задаются в форме похожей на обычный exception и работают через 100500 уровней.
object RestartContext{
private val contextHolder = new ThreadLocal[PartialFunction[Any,Any]]{
override def initialValue() = Map()
}
def context = contextHolder.get
def context_=(value:PartialFunction[Any,Any]){
contextHolder.set(value)
}
}
class CatchHolder[T](body: =>T){
def catchRestarts(catchBody: PartialFunction[Any,Any]):T={
val oldContext = RestartContext.context
RestartContext.context = catchBody.orElse(oldContext)
try{
body
} finally {
RestartContext.context = oldContext
}
}
}
def tryRestarts[T](body: =>T) = new CatchHolder[T](body)
case class NotFoundRestartException() extends RuntimeException
def raiseRestart[T](restart:T):Any={
RestartContext.context.lift(restart) match {
case Some(result)=>result
case None => throw NotFoundRestartException()
}
}
Используем
case class WhatIsNext(value:Int)
abstract sealed trait CuriousRestart
case class NextValue(next:Int) extends CuriousRestart
case object HaveNoIdea extends CuriousRestart
def printNext(value:Int){
// Cannot decide itself, this function is retarded
raiseRestart(WhatIsNext(value)) match {
case NextValue(next) => println("Next for %s is %s".format(value,next))
case HaveNoIdea => throw new IllegalArgumentException("Unable to find next for %s".format(value))
}
}
def main(argv:Array[String]){
tryRestarts{
printNext(10)
printNext(20)
}catchRestarts{
case WhatIsNext(value) if (value<15) => NextValue(value+1)
case WhatIsNext(_) => HaveNoIdea
}
}
Кейс классы необязательны, просто хотел выразить суть
Есть ряд моментов которые выглядят уныло. Испытываю физическую боль от вида [Any,Any], но что тут можно придумать? Ну и ThreadLocal - вынужденное зло. Может кто-то посоветуют как сделать без угрызений совести?
Еще есть ощущение что возможно с грохотом отвалится если просочатся некоторые замкнувшие контекст лямбды с рестартом, но это вроде уже бред.
В отличии от лиспа тут не предусмотрен ручной выбор функции, все в коде.
P.S. Кастую еще имплементации рестартов (возможно ваши) на других ЯП в которых их изначально нету.