Понимание имплицитного в Scala
я пробирался через учебник Scala playframework, и я наткнулся на этот фрагмент кода, который меня озадачил:
def newTask = Action { implicit request =>
taskForm.bindFromRequest.fold(
errors => BadRequest(views.html.index(Task.all(), errors)),
label => {
Task.create(label)
Redirect(routes.Application.tasks())
}
)
}
поэтому я решил исследовать и наткнулся этот пост.
Я все еще не понимаю.
в чем разница между этим:
implicit def double2Int(d : Double) : Int = d.toInt
и
def double2IntNonImplicit(d : Double) : Int = d.toInt
кроме очевидного факта, что они имеют разные имена методов.
когда я должен использовать implicit и зачем?
4 ответов:
я объясню основные случаи использования неявные преобразования, ниже, но для более подробно см. соответствующая глава программирования в 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в область (он также проверяет некоторые другие места как вBиAсопутствующие объекты, если они существуют). Так какdefs может быть "eta-expanded" вFunctionобъекты, animplicit def xyz(arg: B): Aтоже подойдет.Итак, разница между вашими методами заключается в том, что один отмечен
implicitбудет вставлен для вас компилятором, когдаDoubleнайден, но это.implicit def doubleToInt(d: Double) = d.toInt val x: Int = 42.0будет работать то же самое как
def doubleToInt(d: Double) = d.toInt val x: Int = doubleToInt(42.0)во втором мы вставили преобразование вручную; в первом компилятор сделал то же самое автоматически. Преобразование требуется из-за аннотации типа на левой стороне.
что касается вашего первого фрагмента из игры:
действия объясняются на на этой странице из документации по игре (см. также API docs). Вы используете
apply(block: (Request[AnyContent]) ⇒ Result): Action[AnyContent]на
Actionобъект (который является компаньоном к черте того же имени).поэтому нам нужно предоставить функцию в качестве аргумента, который может быть записан как литерал в виде
request => ...в литерале функции, часть перед
=>объявление значения, и может быть отмеченоimplicitесли вы хотите, как и в любой другойvalдекларации. Вот,requestне должны быть отмеченыimplicitдля этого введите check, но при этом это будет доступно как неявное значение для любых методов, которые могут понадобиться в функции (и, конечно же, его можно использовать явно). В данном конкретном случае, это было сделано потому чтоbindFromRequestметод форма класс требует беспрекословного
предупреждение: содержится сарказм рассудительно! МММ...
Луиджи является полным и правильным. Это только для того, чтобы немного расширить его с примером того, как вы можете славно злоупотреблять неявные преобразования, как это часто бывает в проектах 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") }
почему и когда вы должны отмечать
requestпараметр asimplicit:некоторые методы, которые вы будете использовать в организме вашего действия есть неявный список параметров как, например, форма.scala определяет метод:
def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... }вы не обязательно заметите это, как вы бы просто позвонить
myForm.bindFromRequest()вы не должны предоставлять неявные аргументы явно. Нет, ты уходи компилятор искать любого действительного кандидата объект для передачи в каждый раз, когда он сталкивается с вызовом метода, который требует экземпляр запроса. Так как ты do есть запрос доступен, все, что вам нужно сделать, это пометить его какimplicit.вы явно пометить его как доступный для подразумевается использовать.
вы намекаете компилятору, что это "ОК", чтобы использовать объект запроса, отправленный в Play framework (что мы дали имя "запрос", но могли бы использовать только "r" или " req") где потребуется, "потихоньку".
myForm.bindFromRequest()посмотреть? его там нет, но это и там!
это просто происходит без необходимости вставлять его вручную в каждом месте, где это необходимо (но вы можете передать его явно, если вы так хотите, независимо от того, если он отмечен
implicitили нет):myForm.bindFromRequest()(request)не помечая его как неявный, вы бы обязательно сделать выше. Маркировка его как неявного у вас нет к.
, когда если вы пометите запрос как
implicit? Вам действительно нужно только, если вы используете методы, которые объявляют неявный список параметров, ожидающий экземпляр запроса. Но чтобы все было просто, вы можете просто привыкнуть отмечать запросimplicitвсегда. Таким образом, вы можете просто написать красивый лаконичный код.
кроме того, в приведенном выше случае должно быть
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
Comments