Понимание скрытого в Scala

279

Я пробивался через учебник по игре 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 и почему?

Теги:
playframework
syntax
keyword

6 ответов

328
Лучший ответ

Ниже я объясню основные варианты использования последствий, но более подробно см. Соответствующую главу "Программирование в 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 может быть "расширенно" до объектов 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)

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


Относительно вашего первого фрагмента из Play:

Действия описаны на этой странице из документации Play (см. Также документацию по API). Ты используешь

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

на объекте Action (который является дополнением к одноименной черте).

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

request => ...

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

  • 12
    Спасибо за ответ. Ссылка на главу 21 действительно потрясающая. Ценить это.
  • 14
    Просто чтобы добавить это, следующее видео дает отличное объяснение последствий, а также некоторые другие функции scala youtube.com/watch?v=IobLWVuD-CQ
30

ПРЕДУПРЕЖДЕНИЕ: содержит сарказм разумно! YMMV...

Ответ Луиджи является полным и правильным. Это лишь немного, чтобы немного расширить его пример того, как вы можете славно злоупотреблять имплицитами, как это часто случается в проектах 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")
}
  • 1
    Ха-ха. Хорошее чувство юмора.
  • 0
    Я ценю юмор. Подобные вещи - одна из причин, по которой я перестал пытаться изучать Scala много лет назад, и только сейчас возвращаюсь к этому. Я никогда не был уверен, откуда взялись некоторые (многие) следствия в коде, на который я смотрел.
5

Почему и когда вы должны пометить параметр request как implicit:

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

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

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

Вы явно помечены как доступные для неявного использования.

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

myForm.bindFromRequest()

видеть это? он не там, но он есть!

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

myForm.bindFromRequest()(request)

Не помещая его как неявное, вы должны выполнить описанное выше. Помечая это как неявное, вам не нужно.

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

  • 0
    «Таким образом, вы можете просто написать красивый краткий код». Или, как указывает @DanielDinnyes, красиво запутанный код. Может быть очень трудно отследить, откуда исходит неявное, и они могут фактически усложнить чтение и поддержку кода, если вы не будете осторожны.
2

Кроме того, в приведенном выше случае должна существовать 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
0

Лучший ответ здесь

Это лучший ответ, который я когда-либо читал. Это было описано пошагово. Полагаю, что после прочтения этого ответа никаких проблем с неявным не останется :)

0

Изображение 6061

Более подробное и ясное понимание дано здесь в этом посте.

о implicit- более ясном понимании

Ещё вопросы

Сообщество Overcoder
Наверх
Меню