2012-04-29 2 views
241

я делал свой путь через Scala playframework учебник, и я наткнулся на этот фрагмент кода, который имел меня озадачило:Понимание заложенная в 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()) 
     } 
) 
} 

Поэтому я решил исследовать и наткнулся на this post.

Я все еще не понимаю.

В чем разница между этим:

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

и

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

кроме того очевидного факта, что они имеют разные имена методов.

Когда следует использовать implicit и почему?

ответ

313

Я объясню основные случаи использования имплицитов ниже, но более подробно см. relevant chapter of Programming in Scala.

Неявных параметры

Окончательный список параметров по методе могут быть отмечены implicit, что означает, что значения будут взяты из контекста, в котором они называются. Если в сфере видимости нет значения неявного значения, он не будет компилироваться. Поскольку неявное значение должно быть разрешено к одному значению и избегать столкновений, целесообразно сделать тип специфичным для своей цели, например. не требуйте, чтобы ваши методы нашли неявный Int!

пример:

// 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" 

Неявные преобразования

Когда компилятор находит выражение неправильного типа для контекста, он будет искать неявное Function значения такого типа, который позволит его к typecheck. Поэтому, если требуется A, и он найдет B, он будет искать неявное значение типа B => A в области видимости (он также проверяет некоторые другие места, например, в сопутствующих объектах и A, если они существуют). Так как def s могут быть «eta-extended» в Function объектах, то и implicit def xyz(arg: B): A.

Таким образом, разница между вашими методами заключается в том, что один из отмеченных implicit будет вставлен вам компилятором, когда будет найден Double, но требуется Int.

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

будет работать так же, как

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

Во втором мы вставили преобразование вручную; в первом компилятор сделал то же самое автоматически. Преобразование требуется из-за аннотации типа с левой стороны.


Что касается вашего первого фрагмента из игры:

действия объясняются на this page из документации Play (также см API docs). Вы используете

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

на Action объекта (который является спутником признака того же имени).

Таким образом, мы должны предоставить функции в качестве аргумента, который может быть записан в виде литерала в виде

request => ... 

В функции буквального, часть перед => является декларацией значение, и может быть пометьте implicit, если хотите, как и в любой другой декларации val. Здесь не имеет для этого, чтобы ввести тип проверки, но при этом будет доступным как неявное значение для любых методов, которые могут понадобиться ему в пределах функции (и, конечно, это может также будет использоваться явно). В этом конкретном случае это было сделано, потому что метод bindFromRequest класса Form требует неявного аргумента Request.

+12

Благодарим за отзыв. Ссылка на главу 21 действительно потрясающая. Ценить это. – Clive

+13

Чтобы добавить это, следующее видео дает отличное объяснение имплицитов плюс некоторые другие особенности scala. Http://www.youtube.com/watch?v=IobLWVuD-CQ – Shakti

3

Почему и когда следует отметить request параметр как implicit:

Некоторые методы, которые будут использовать в теле ваших действий есть список неявный параметр как, например, Form.scala Определяет метод:

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

вы не обязательно заметите это, как вы бы просто позвонить myForm.bindFromRequest() вы не должны предоставлять неявные аргументы в явном виде. Нет, вы оставляете компилятору для поиска любого действительного объекта-кандидата для передачи при каждом вызове метода, требующем экземпляра запроса. Поскольку у вас есть do, у вас есть запрос, все что вам нужно сделать, это отметить его как implicit.

Вы явно отметьте его как можно для неявным.

Вы намекаете компилятору, что это «ОК», чтобы использовать объект запроса, отправленный платформой Play (мы дали имя «запрос», но могли бы использовать только «r» или «req») везде, где это требуется, втихаря".

myForm.bindFromRequest() 

посмотрите? его нет, но это есть!

Это просто происходит без того, чтобы прорезать его вручную в каждом месте, это необходимо (но вы можете передать его в явном виде, если вы так хотите, независимо от того, если оно помечено implicit или нет):

myForm.bindFromRequest()(request) 

Без маркировки как неявные, вы должны были бы до выполнить вышеуказанное. Отмечая это как неявное, вам это не нужно.

Когда необходимо указать как implicit? Вам действительно нужно, если вы используете методы, объявляющие список неявных параметров , ожидающий экземпляр запроса. Но чтобы это было просто, вы могли просто привыкнуть отмечать запрос implicitвсегда. Таким образом, вы можете просто написать красивый краткий код.

19

ВНИМАНИЕ: содержит сарказм разумно! YMMV ...

Luigi's answer является полным и полным. Это лишь немного, чтобы немного увеличить пример того, как вы можете славно злоупотреблять implicits, как это часто случается в проектах Scala. На самом деле так часто вы можете даже найти его в одном из руководства «Лучшая практика».

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

Ха-ха. Хорошее чувство юмора. – Det

0

Кроме того, в приведенном выше случае должно быть only one неявной функции, тип которой double => Int. В противном случае компилятор запутывается и не будет компилироваться должным образом.

//this won't compile 

implicit def doubleToInt(d: Double) = d.toInt 
implicit def doubleToIntSecond(d: Double) = d.toInt 
val x: Int = 42.0 
Смежные вопросы