Scala: передача одного неявного параметра неявно, а другого явно. Является ли это возможным?

Рассмотрим функцию:

def foo(implicit a:Int, b:String) = println(a,b).

Теперь предположим, что в области действия есть неявные String и Int (implicit val i1=1), но мы хотим явно передать другое, не неявное Int (val i2=2) в foo .

Как мы можем сделать это ? Является ли это возможным? Спасибо за чтение.


person jhegedus    schedule 21.03.2014    source источник
comment
Было бы неплохо, если бы будущая версия Scala поддерживала использование именованных аргументов в этом случае, например. implicit val a = 33; foo(b = "hello"). Не могу представить случая, когда это было бы проблематично.   -  person 0__    schedule 17.09.2014


Ответы (5)


Все, что я могу добавить, это:

def foo(implicit a: Int, b: String) = println(a, b)
implicit val i1 = 1
implicit val s = ""
val i2 = 2
foo(i2, implicitly[String])
person Peter Schmitz    schedule 21.03.2014
comment
И знаете сумасшедшую вещь? На самом деле вы даже можете избежать избыточного указания типа аргумента. Другими словами, следующее компилируется нормально: foo(i2, implicitly), здесь не нужно делать foo(i2, implicitly[String]) - person Régis Jean-Gilles; 21.03.2014

Если у вашего метода много неявных параметров (у меня иногда есть в моих проектах), и иногда вы хотите просто указать один из них явно, а остальные разрешить неявно, вы можете написать implicitly для каждого другого параметра, как показано в моем другом ответе. Но иногда вы измените сигнатуру этого метода или явный параметр находится в середине этого списка параметров, тогда вы можете сделать более читаемый клиентский код со следующей конструкцией:

Предположим, у вас есть несколько типов и их неявные фиктивные объекты:

trait I1; implicit object I1 extends I1
trait I2; implicit object I2 extends I2
trait I3; implicit object I3 extends I3
trait I4; implicit object I4 extends I4
trait I5; implicit object I5 extends I5
trait I6; implicit object I6 extends I6

Теперь у вас есть метод foo1, который использует следующие имплициты:

def foo1(implicit i1: I1, i2: I2, i3: I3, i4: I4, i5: I5, i6: I6) {
  println(i1, i2, i3, i4, i5, i6)
}

Теперь вы часто хотите явно указать i4: I4. Итак, вы пишете:

val i4 = new I4 {}
foo1(implicitly, implicitly, implicitly, i4, implicitly, implicitly)

Уродливо!
Со следующей (должна быть помещена в область действия метода foo2 и, возможно, переименована) оболочка для всех имплицитов:

object Implicits {
  def apply(i4: I4)(implicit i1: I1, i2: I2, i3: I3, i5: I5, i6: I6) = new Implicits(i1, i2, i3, i4, i5, i6)
  implicit def applying(implicit i1: I1, i2: I2, i3: I3, i4: I4, i5: I5, i6: I6) = new Implicits(i1, i2, i3, i4, i5, i6)
}
class Implicits(val i1: I1, val i2: I2, val i3: I3, val i4: I4, val i5: I5, val i6: I6)

и связанный метод foo2:

def foo2(implicit implicits: Implicits) = {
  import implicits._
  println(i1, i2, i3, i4, i5, i6)
}

теперь вы можете вызывать foo2 вместо foo1 следующим образом:

locally {
  foo2 // using implicit dummy objects I1, ..., I6 from above
  // or with explicit I4:
  val i4 = new I4 {}
  foo2(Implicits(i4))
}
person Peter Schmitz    schedule 21.03.2014

  1. Явно вызовите foo(i2, s1), но вы потеряете преимущество использования implicit String
  2. Определите def foo1(a: Int)(implicit b: String)=foo(a,b), и вы позвоните foo1(i2)
person Bruno Grieder    schedule 21.03.2014

Вы можете создать новую внутреннюю область и определить в ней новый implicit val. Преимущество заключается в том, что когда у вас есть несколько вызовов функций, вы можете переопределить один неявный для всех из них в одном месте:

def foo(implicit a:Int, b:String) = println(a,b).

implicit val i = 1
implicit val s = ""

foo // call with original implicits

{
  implicit val i = 2

  foo // call with a new Int implicit

  foo // call with a new Int implicit again

}

Примечание: новая неявная переменная должна иметь то же имя переменной, что и исходная, чтобы она ее скрывала, иначе вы получите ошибку компилятора о неоднозначных неявных значениях.

person Suma    schedule 10.03.2017

Я знаю, что это старый вопрос, но он все еще может быть интересным. Хороший способ сделать это - использовать неявно в качестве значения по умолчанию:

scala> def foo(a: Int = implicitly[Int], b: String = implicitly[String]) = println(a,b)

scala> foo()
(10,boo)

scala> foo(50)
(50,boo)

scala> foo(b="bar")
(10,bar)
person roterl    schedule 13.09.2014
comment
Не совсем - в этом случае неявный параметр должен быть в области видимости на сайте объявления метода, а не на вашем обычном поведении на сайте вызова метода. - person Eric W; 21.11.2014