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

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

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 класс, например.

2 ответа

Решение

Я нашел обходной путь:

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"), из-за синтаксиса странности, объясненной здесь.

Это плохая идея использовать такой общий тип как 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") 
Другие вопросы по тегам