Понимание имплицитного в 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 и зачем?

652   4  

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 сопутствующие объекты, если они существуют). Так как def s может быть "eta-expanded" в Function объекты, an implicit 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 параметр as implicit:

некоторые методы, которые вы будете использовать в организме вашего действия есть неявный список параметров как, например, форма.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

    Ничего не найдено.