Why prefer implicit val over implicit object

时光毁灭记忆、已成空白 提交于 2021-02-07 04:14:51

问题


When asking questions about implicits a common suggestion / recommendation / advice that is given together with the answer (or sometimes that is the answer itself) is to use implicit vals with excplicit type signatures instead of using implicit objects.

But, what is the reason behind that?


回答1:


"TL;DR;" The reason is that an implici val with an explicit type signature has the exact type you want whereas an implicit object has a different type.

The best way to show why that could be a problem is with a simple example:

// Having the following definitions.

trait SomeTrait[T] {
  def f: T
}

trait ExtendedTrait[T] extends SomeTrait[T] {
  def g: T
}

implicit val SomeStringVal: SomeTrait[String] = new SomeTrait[String] {
  override def f: String = "from val f"
}

implicit val ExtendedStringVal: ExtendedTrait[String] = new ExtendedTrait[String] {
  override def f: String = "from extended val f"
  override def g: String = "from extended val g"
}

implicit object ExtendedStringObject extends ExtendedTrait[String] {
  override def f: String = "from extended obj f"
  override def g: String = "from extended obj g"
}

// What will be the result of:
implicitly[SomeTrait[String]].f

Remember that:

If there are several eligible arguments which match the implicit parameter's type, the most specific one will be chosen using the rules of static overloading resolution.

Well then, the answer is: "from extended obj f".
The above (somewhat surprising) result, is caused because objects have their own type (singleton type ExtendedStringObject.type which is a subtype of ExtendedTrait[String]), as such, it is more "specific" than the others.

Now, the reason why that could be a problem is that most people are not aware that the object has its own type and that it is more specific than what they believe. Nevertheless, this could also make the object to be picked when you would not want it to be; for example:

// If instead we only had this:

implicit val ExtendedStringVal: ExtendedTrait[String] = new ExtendedTrait[String] {
  override def f: String = "from extended val f"
  override def g: String = "from extended val g"
}

implicit object ExtendedStringObject extends SomeTrait[String] {
  override def f: String = "from extended obj f"
}

// Then what will be the result of:
implicitly[SomeTrait[String]].f

In this case, we will have an "ambiguous implicit values" exception; because both options are equally specific.


Is the rules universal?

No.

As with most things in software engineering, nothing is universal.
There are cases where using an implicit val with an explicit type signature is either not possible (for example, because the type is not writable in source code even if the compiler knows it exists) or it would not produce the correct result whereas an implicit object would do.
A simple example would be:

trait A {
  type X
}

object A {
  type Aux[XX] = A { type X = XX }
}

class B extends A {
  type X = T

  class T
}

implicit object b extends B
implicit object b1 extends B

Such that you can ask for implicitly[A.Aux[b.T]] whereas using implicit val b: B = new B {} would have not worked.
(code running here full context here).

However, it could be said that those are rare (advanced) cases. As such, this could be considered a good guideline.



来源:https://stackoverflow.com/questions/65258339/why-prefer-implicit-val-over-implicit-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!