2011-07-27 15 views
11

Estoy tratando de usar las continuidades de Scala (2.9.0) para crear una API aparentemente bloqueante, pero que en realidad es asíncrona. Supongamos que le gustaría escribir algo como:Uso de continuación de Scala para API sin bloqueo

if(ask("Continue?")) //Prompts Yes/No 
    name = input("Enter your name") 

Dónde ask devuelve un valor lógico si el usuario pulsa sí, y input pide un valor. Imagine que se llama desde un servidor web, donde ask y input no bloquean ningún subproceso, simplemente almacenan una continuación en un Mapa (o la sesión, no importa mucho) antes de mostrar la página con el aviso (liberando la mayoría de los recursos) . Y cuando una respuesta ha regresado, busca la continuación en el mapa y reanuda el código.

El problema hasta ahora es que parece que no puedo encontrar una forma adecuada de definir ask y input para usar las continuaciones sin pasar el tipo de retorno del contexto de la llamada como parámetro.

El más cercano que tenemos es hacer algo como:

#!/bin/sh 
exec scala -P:continuations:enable -deprecation "$0" "[email protected]" 
!# 
import util.continuations._ 

//Api code 
def display[T](prompt: String) = shift { 
    cont: (Unit => T) => { 
     println(prompt) 
     cont() 
    } 
} 

//Client code 
def foo() : Int = reset { 
    display[Int]("foo!") // <-- how do I get rid of the type annotation? 
    5 
} 

def bar() : Unit = reset { 
    display[Unit]("bar!") 
} 

println(foo()) 
bar() 

Realmente me gustaría deshacerse de la anotación de tipo de llamadas a display. ¿Alguien sabe de una manera de lograr esto? No me importa si la definición de la API se vuelve más fea, siempre que el código del cliente sea más simple. Gracias!

+2

Publique su respuesta como respuesta. –

+0

¡No sabía que podría! – juancn

Respuesta

6

finalmente lo he descubierto:

#!/bin/sh 
exec scala -P:continuations:enable -deprecation "$0" "[email protected]" 
!# 
import util.continuations._ 

class Display(val resume: (Unit => Any)) extends Throwable 

//Api code 
def display(prompt: String) = shift { 
    cont: (Unit => Any) => { 
     println(prompt) 
     throw new Display(cont) 
    } 
} 

//Client code 
def foo() : Int = reset { 
    display("foo!") 
    5 
} 

def bar() : Unit = reset { 
    display("bar!") 
} 

//Framework 
try { 
    foo() 
} catch { 
    case d: Display => println(d.resume()) 
} 

try { 
    bar() 
} catch { 
    case d: Display => d.resume() 
} 

El truco está aceptando métodos que devuelven Any (Homeresque: D'oh) y volviendo Nothing.

Si desea poner en práctica algo que devuelve un valor, como ask, que puede hacer:

class Ask(val resume: (Boolean => Any)) extends Throwable 

//Api code 
def ask(prompt: String) = shift { 
    cont: (Boolean => Any) => { 
     println(prompt) 
     throw new Ask(cont) 
    } 
} 

En el código anterior, pida devuelve un Boolean.