2012-04-29 30 views
241

yo estaba haciendo mi camino a través del tutorial Scala playframework y me encontré con este fragmento de código que me había intrigado:entendimiento implícito en Scala

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 decidió investigar y se encontró con this 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 que tienen diferentes nombres de método.

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

Respuesta

313

Explicaré los principales casos de uso de implicitos a continuación, pero para más detalles, vea el relevant chapter of Programming in Scala.

parámetros implícitos

La lista de parámetros final sobre un método se pueden marcar implicit, que significa que los valores se toman a partir del contexto en el que se les llama. 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 único valor y para evitar conflictos, es una buena idea hacer que el tipo sea específico para su propósito, p. no requiera sus métodos para encontrar un implícito Int!

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, se buscará un valor implícito Function de un tipo que le permitirá para tipear Entonces, si se requiere un A y encuentra un 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 pueden ser "expandidos eta" en objetos Function, también funcionará un implicit def xyz(arg: B): A.

Así que la diferencia entre sus métodos es que el compilador insertará uno marcado implicit cuando se encuentre un Double pero se requiere 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 la segunda 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.


En cuanto a su primer fragmento de Juego:

acciones se explican en this page de la documentación de reproducción (véase también API docs). Está utilizando

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

en el objeto Action (que es el compañero con el rasgo del mismo nombre).

por lo que necesitamos para suministrar una función como argumento, que puede escribirse como un literal en forma

request => ... 

En un literal de la función, la parte antes del => es una declaración de valor, y puede ser marcó implicit si lo desea, al igual que en cualquier otra declaración val. Aquí, requestno se tiene que marcar implicit para que este tipo de verificación, pero al hacerlo será disponible como un valor implícito para cualquier método que pueda necesitarlo dentro de la función (y, por supuesto, puede ser utilizado explícitamente también). En este caso particular, esto se ha hecho porque el método bindFromRequest en la clase Form requiere un argumento implícito Request.

+12

Gracias por la respuesta. El enlace para el capítulo 21 es realmente increíble. Lo aprecio. – Clive

+13

Solo para agregar esto, el siguiente video brinda una excelente explicación de las implicaciones más algunas otras características de scala http://www.youtube.com/watch?v=IobLWVuD-CQ – Shakti

3

¿Por qué y cuándo debe marcar los parámetros request como implicit:

Algunos métodos que va a hacer uso de en el cuerpo de su acción tenga una lista parámetro implícito como, por ejemplo, define Form.scala un método:

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

usted no necesariamente cuenta de esto como usted acaba de llamar myForm.bindFromRequest() usted no tiene que proporcionar los argumentos implícitos de forma explícita. No, deje el compilador para buscar cualquier objeto candidato válido que pase cada vez que se encuentre con una llamada a un método que requiera una instancia de la solicitud. Dado que tiene tiene una solicitud disponible, todo lo que necesita hacer es marcarlo como implicit.

Usted explícitamente marca como disponible para implícita uso.

Indique el compilador que está "bien" para usar el objeto de solicitud enviado por Play framework (que le dimos el nombre "solicitud" pero podría haber usado simplemente "r" o "req") donde sea necesario " a hurtadillas".

myForm.bindFromRequest() 

ver? no está allí, pero es allí!

simplemente sucede sin que tenga a la ranura en forma manual en cada lugar se necesita (pero puede pasarlo explícitamente, si así lo desea, sin importar si está marcada implicit o no):

myForm.bindFromRequest()(request) 

Sin marcarlo como implícito, debería tener que hacer lo anterior. Marcándolo como implícito, no es necesario.

¿Cuándo debe marcar la solicitud como implicit? Solo necesita hacerlo si está utilizando métodos que declaran una lista de parámetros implícitos esperando una instancia de la Solicitud. Pero para mantenerlo simple, podría simplemente adquirir el hábito de marcar la solicitud implicitsiempre. De esta forma, puedes escribir un hermoso código de acceso rápido.

19

ADVERTENCIA: contiene sarcasmo juiciosamente! YMMV ...

Luigi's answer es completo y correcto. Este es solo para ampliarlo un poco con un ejemplo de cómo se puede abusar gloriosamente de implicits, ya que ocurre con bastante frecuencia en los proyectos de Scala. De hecho, tan a menudo, incluso puede encontrarlo en una de las guías "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") 
} 
+0

Haha. Buen sentido del humor. – Det

0

También, en el caso anterior no debería ser only one función implícita cuyo tipo es double => Int. De lo contrario, el compilador se confunde y no se compilará correctamente.

//this won't compile 

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