twirl play framework scala syntax playframework keyword

play - Entendimiento implícito en Scala



play framework scala forms (4)

A continuación, explicaré los principales casos de uso de los implícitos, pero para más detalles, consulte el capítulo relevante de Programación en Scala .

Parámetros implícitos

La lista final de parámetros en un método se puede marcar como implicit , lo que significa que los valores se tomarán del contexto en el que se llaman. Si no hay un valor implícito del tipo correcto en el alcance, no se compilará. Dado que el valor implícito debe resolverse en un solo valor y para evitar choques, es una buena idea hacer que el tipo sea específico para su propósito, por ejemplo, ¡no requiera que sus métodos encuentren un Int implícito!

ejemplo:

// probably in a library class Prefixer(val prefix: String) def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s // then probably in your application implicit val myImplicitPrefixer = new Prefixer("***") addPrefix("abc") // returns "***abc"

Conversiones implícitas

Cuando el compilador encuentra una expresión del tipo incorrecto para el contexto, buscará un valor de Function implícito de un tipo que le permitirá verificar con la tipografía. Entonces, si se requiere una A y encuentra una B , buscará un valor implícito de tipo B => A en el alcance (también verifica otros lugares como en los objetos complementarios B y A , si existen). Como def s puede ser "eta-expandido" en objetos de Function , una implicit def xyz(arg: B): A también funcionará.

Entonces, la diferencia entre sus métodos es que el compilador insertará el marcado como implicit cuando se encuentre un Double pero se requiera un Int .

implicit def doubleToInt(d: Double) = d.toInt val x: Int = 42.0

funcionará igual que

def doubleToInt(d: Double) = d.toInt val x: Int = doubleToInt(42.0)

En el segundo hemos insertado la conversión manualmente; en el primero el compilador hizo lo mismo automáticamente. La conversión es necesaria debido a la anotación de tipo en el lado izquierdo.

Respecto a tu primer fragmento de Play:

Las acciones se explican en esta página de la documentación de Play (consulte también los documentos de la API ). Tu estas usando

apply(block: (Request[AnyContent]) ⇒ Result): Action[AnyContent]

en el objeto Action (que es el complemento del rasgo del mismo nombre).

Por lo tanto, necesitamos proporcionar una función como argumento, que se puede escribir como un literal en la forma

request => ...

En una función literal, la parte anterior a => es una declaración de valor, y puede marcarse como implicit si lo desea, como en cualquier otra declaración de val . En este caso, la request no tiene que estar marcada como implicit para que este escriba la verificación, pero al hacerlo estará disponible como un valor implícito para cualquier método que pueda necesitarla dentro de la función (y, por supuesto, puede usarse explícitamente como bien). En este caso particular, esto se hizo porque el método bindFromRequest en la clase Form requiere un argumento de Request implícito.

Me estaba abriendo camino a través del tutorial de Scala playframework y me encontré con este fragmento de código que me tenía desconcertado:

def newTask = Action { implicit request => taskForm.bindFromRequest.fold( errors => BadRequest(views.html.index(Task.all(), errors)), label => { Task.create(label) Redirect(routes.Application.tasks()) } ) }

Así que decidí investigar y me encontré con este post .

Todavía no lo entiendo.

¿Cuál es la diferencia entre esto?

implicit def double2Int(d : Double) : Int = d.toInt

y

def double2IntNonImplicit(d : Double) : Int = d.toInt

Aparte del hecho obvio, tienen diferentes nombres de métodos.

¿Cuándo debo usar implicit y por qué?


Además, en el caso anterior, only one debe haber only one función implícita cuyo tipo sea double => Int . De lo contrario, el compilador se confunde y no compila correctamente.

//this won''t compile implicit def doubleToInt(d: Double) = d.toInt implicit def doubleToIntSecond(d: Double) = d.toInt val x: Int = 42.0


Por qué y cuándo debe marcar el parámetro de request como implicit :

Algunos métodos que utilizará en el cuerpo de su acción tienen una lista de parámetros implícita como, por ejemplo, Form.scala define un método:

def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... }

No necesariamente se da cuenta de esto, ya que simplemente llamaría a myForm.bindFromRequest() No tiene que proporcionar los argumentos implícitos explícitamente. No, deja que el compilador busque cualquier objeto candidato válido para pasar cada vez que se encuentre con una llamada de método que requiera una instancia de la solicitud. Como tiene una solicitud disponible, todo lo que necesita hacer es marcarla como implicit .

Lo marca explícitamente como disponible para uso implícito .

Indica al compilador que está "OK" para usar el objeto de solicitud enviado por el marco de Play (que le dimos el nombre de "solicitud" pero que podría haber usado solo "r" o "req") donde sea necesario, "a escondidas" .

myForm.bindFromRequest()

¿Míralo? ¡No está ahí, pero está ahí!

Simplemente sucede sin tener que insertarlo manualmente en cada lugar que sea necesario (pero puede pasarlo explícitamente, si así lo desea, sin importar si está marcado como implicit o no):

myForm.bindFromRequest()(request)

Sin marcarlo como implícito, tendrías que hacer lo anterior. Marcándolo como implícito no tienes que hacerlo.

¿Cuándo deberías marcar la solicitud como implicit ? Solo debe hacerlo si está utilizando métodos que declaran una lista de parámetros implícitos que esperan una instancia de la Solicitud . Pero para mantenerlo simple, simplemente puede adquirir el hábito de marcar la solicitud implicit siempre . De esa manera usted puede escribir código terso hermoso.


ADVERTENCIA: contiene sarcasmo juiciosamente! YMMV ...

La respuesta de Luigi es completa y correcta. Esto es solo para extenderlo un poco con un ejemplo de cómo se puede sobreutilizar gloriosamente los implícitos , como sucede con frecuencia en los proyectos de Scala. En realidad, muy a menudo, es probable que incluso pueda encontrarlo en una de las guías de "Mejores prácticas" .

object HelloWorld { case class Text(content: String) case class Prefix(text: String) implicit def String2Text(content: String)(implicit prefix: Prefix) = { Text(prefix.text + " " + content) } def printText(text: Text): Unit = { println(text.content) } def main(args: Array[String]): Unit = { printText("World!") } // Best to hide this line somewhere below a pile of completely unrelated code. // Better yet, import its package from another distant place. implicit val prefixLOL = Prefix("Hello") }