Estoy tratando de representar una función que no toma argumentos y no devuelve ningún valor (estoy simulando la función setTimeout en JavaScript, si debe saberlo).
case class Scheduled(time : Int, callback : => Unit)
no se compila, diciendo que "los parámetros 'val' pueden no ser llamados por nombre"
case class Scheduled(time : Int, callback : () => Unit)
compila, pero tiene que ser invocado de forma extraña, en lugar de
Scheduled(40, { println("x") } )
Tengo que hacer esto
Scheduled(40, { () => println("x") } )
Lo que también funciona es
class Scheduled(time : Int, callback : Unit => Unit)
pero se invoca de una manera aún menos sensata
Scheduled(40, { x : Unit => println("x") } )
(¿Cuál sería una variable de tipo Unidad?) Lo que quiero por supuesto, es un constructor que se pueda invocar de la forma en que lo invocaría si fuera una función ordinaria:
Scheduled(40, println("x") )
¡Dale al biberón su bebé!
case class Scheduled(time: Int)(callback: => Unit)
. Esto funciona porque la lista de parámetros secundarios no se expone públicamente, ni se incluye en los métodosequals
/ generadoshashCode
.Respuestas:
Llamada por nombre: => Tipo
La
=> Type
notación significa llamada por nombre, que es una de las muchas formas en que se pueden pasar los parámetros. Si no está familiarizado con ellos, le recomiendo que se tome un tiempo para leer ese artículo de Wikipedia, aunque hoy en día es principalmente llamada por valor y llamada por referencia.Lo que significa es que lo que se pasa se sustituye por el nombre del valor dentro de la función. Por ejemplo, tome esta función:
Si lo llamo así
Entonces el código se ejecutará así
Aunque eso plantea el punto de lo que sucede si hay un choque de nombre de identificador. En la llamada tradicional por nombre, se lleva a cabo un mecanismo llamado sustitución para evitar la captura para evitar conflictos de nombres. En Scala, sin embargo, esto se implementa de otra manera con el mismo resultado: los nombres de los identificadores dentro del parámetro no pueden referirse a los identificadores de sombra en la función llamada.
Hay algunos otros puntos relacionados con la llamada por nombre de los que hablaré después de explicar los otros dos.
Funciones 0-arity: () => Tipo
La sintaxis
() => Type
representa el tipo de aFunction0
. Es decir, una función que no toma parámetros y devuelve algo. Esto es equivalente a, por ejemplo, llamar al métodosize()
: no toma parámetros y devuelve un número.Sin embargo, es interesante que esta sintaxis es muy similar a la sintaxis de una función literal anónima , lo que es motivo de cierta confusión. Por ejemplo,
es una función anónima literal de arity 0, cuyo tipo es
Entonces podríamos escribir:
Sin embargo, es importante no confundir el tipo con el valor.
Unidad => Tipo
Esto es en realidad solo un
Function1
, cuyo primer parámetro es de tipoUnit
. Otras formas de escribirlo serían(Unit) => Type
oFunction1[Unit, Type]
. La cosa es ... es poco probable que esto sea lo que uno quiere. ElUnit
propósito principal del tipo es indicar un valor que no le interesa, por lo que no tiene sentido recibir ese valor.Considere, por ejemplo,
¿Con qué se podría hacer
x
? Solo puede tener un valor único, por lo que no es necesario recibirlo. Un posible uso sería encadenar funciones que devuelvenUnit
:Debido a
andThen
que solo se define enFunction1
y las funciones que estamos encadenando están regresandoUnit
, tuvimos que definirlas como de tipoFunction1[Unit, Unit]
para poder encadenarlas.Fuentes de confusión
La primera fuente de confusión es pensar que la similitud entre el tipo y el literal que existe para las funciones de aridad 0 también existe para la llamada por nombre. En otras palabras, pensar eso, porque
es un literal para
() => Unit
, entoncessería un literal para
=> Unit
. No lo es. Ese es un bloque de codigo , no un literal.Otra fuente de confusión es que
Unit
el valor de ese tipo está escrito()
, lo que parece una lista de parámetros de 0 aridades (pero no lo es).fuente
case ... =>
, así que no lo mencioné. Triste pero cierto. :-)1
, el carácter'a'
, la cadena"abc"
o la función() => println("here")
, para algunos ejemplos). Se puede pasar como argumento, almacenarse en variables, etc. Un "bloque de código" es una delimitación sintáctica de declaraciones: no es un valor, no se puede pasar ni nada por el estilo.(Unit) => Type
vs() => Type
: la primera es aFunction1[Unit, Type]
, mientras que la segunda es aFunction0[Type]
.El
case
modificador hace implícitoval
de cada argumento al constructor. Por lo tanto (como alguien señaló) si eliminacase
puede usar un parámetro de llamada por nombre. El compilador probablemente podría permitirlo de todos modos, pero podría sorprender a las personas si se creara enval callback
lugar de transformarselazy val callback
.Cuando cambia a
callback: () => Unit
ahora, su caso solo toma una función en lugar de un parámetro de llamada por nombre. Obviamente, la función se puede almacenarval callback
para que no haya ningún problema.La forma más fácil de obtener lo que desea (
Scheduled(40, println("x") )
donde se usa un parámetro de llamada por nombre para pasar una lambda) es probablemente omitircase
y crear explícitamente loapply
que no pudo obtener en primer lugar:En uso:
fuente
En la pregunta, desea simular la función SetTimeOut en JavaScript. Basado en respuestas anteriores, escribo el siguiente código:
En REPL, podemos obtener algo como esto:
Nuestra simulación no se comporta exactamente igual que SetTimeOut, porque nuestra simulación es función de bloqueo, pero SetTimeOut no es de bloqueo.
fuente
Lo hago de esta manera (simplemente no quiero romper aplicar):
y llámalo
fuente