Подтвердить что ты не робот

Как предоставить значение по умолчанию для неявных параметров на уровне класса

Я пытаюсь определить класс с некоторыми методами, принимающими неявный параметр:

object Greetings {
  def say(name: String)(implicit greetings: String): String = greetings + " " +name 
}

Я использую этот класс из другого класса

implicit val greetings = "hello"                //> greetings  : java.lang.String = hello
Greetings.say("loic")                           //> res0: String = hello loic
Greetings.say("loic")("hi")                     //> res1: String = hi loic

Моя проблема в том, что она работает, только если я определяю неявный val вне моего объекта Greetings. Я хотел бы иметь возможность предоставлять методы с неявными параметрами со значением по умолчанию внутри моего класса, чтобы упростить использование моего API (например, API-интерфейс Scala).

Итак, я хотел бы сделать это, но он не работает (неявное значение не найдено):

object Greetings {
  implicit val greetings = "hello"    
  def say(name: String)(implicit greetings: String): String = greetings + " " +name 
}

а затем

Greetings.say("loic")                         
Greetings.say("loic")("hi") 

Я знаю, что могу определить значение по умолчанию с помощью (implicit greetings: String = "hello"), но я хотел бы сделать это на уровне класса, чтобы избежать повторения, если существует много методов.

Я предполагаю, что у меня что-то не хватает, потому что я видел, что CanBuildFrom определяется внутри класса List, например.

4b9b3361

Ответ 1

Я нашел обходное решение:

class Greetings(implicit val greetings: String = "hello") {
    def say(name: String): String = greetings + " " + name 
}

Подобно этому, я могу иметь значение по умолчанию и переопределять его, если хочу:

new Greetings().say("loic")                     //> res0: String = hello loic

implicit val greetings = "hi"                   //> greetings  : java.lang.String = hi
new Greetings().say("loic")                     //> res1: String = hi loic

new Greetings()("coucou").say("loic")           //> res2: String = coucou loic

Примечание: new Greetings()("coucou") работает, а не new Greetings("coucou"), из-за синтаксической странности, объясненной здесь.

Ответ 2

Плохая идея использовать такой общий тип, как String в неявной. Основная причина заключается в том, что неявный поиск основан исключительно на типе, так что, если кто-то еще определяет другое неявное значение типа String? Вы можете столкнуться с конфликтом. Поэтому вы должны определить свой собственный тип для своей собственной цели (простая оболочка вокруг String).

Другая причина заключается в том, что при поиске неявных значений компилятор будет искать (среди других мест) в сопутствующем объекте (если есть) типа неявного значения. Вы можете легко увидеть, насколько это полезно, поскольку объект-компаньон является естественным местом для размещения неявного значения по умолчанию (как в вашем случае). Но если неявное значение имеет тип, который у вас нет (например, String), вы просто не можете написать для него объект-компаньон, тогда как с вашим собственным типом-оболочкой проблем нет.

Хорошо, достаточно слов, вот как вы можете это сделать:

case class Greetings( value: String ) {
  override def toString = value
}
object Greetings {
  // this implicit is just so that we don't have to manually wrap 
  // the string when explicitly passing a Greetings instance
  implicit def stringToGreetings( value: String ) = Greetings( value ) 

  // default implicit Greetings value
  implicit val greetings: Greetings ="hello"

  def say(name: String)(implicit greetings: Greetings): String = greetings + " " +name 
}
Greetings.say("loic")                         
Greetings.say("loic")("hi")