简体   繁体   中英

In scala 2.13, how to use implicitly[value singleton type]?

The following simple code:

  implicit val a: String = "abc"

  implicitly[a.type]

fails to compile despite that a is totally in the scope and consistent in type:

Error:(9, 13) could not find implicit value for parameter e: 
...AP.a.type with Singleton
  implicitly[a.type with Singleton]

It appears that this inconsistent behaviour is deliberate. What's the point of this design? What is the shortest change I can do to make it compile?

UPDATE 1 : I just realised that the annotation "String" is the culprit, the following code total worked:

  val a: String = "abc"

  implicit val aa: a.type = a

  implicitly[a.type]

Unfortunately it contains a lot of duplicated definition, is there any chance to make it shorter?

Thanks a lot for your help.

fails to compile despite that a is totally in the scope and consistent in type:

It is NOT consistent in type.

Consider example

trait Parent
trait Child extends Parent

{
  implicit val p: Parent = null
  // implicitly[Child] // doesn't compile
}

{
  implicit val c: Child = null
  implicitly[Parent] // compiles
}

Similarly in our case a.type <: String , you declared implicit of type String , so implicit of type a.type is not found.

If you have an implicit of some type it will work also for all supertypes but will not work for all subtypes (strictly). This is just Liskov principle . That's why you shouldn't look for implicit of type Any or define implicit of type Nothing .

Similarly, if a type class is covariant then all supertypes of an instance of this type class are also its instances

trait TC[+A]

{
  implicit val inst: TC[Parent] = null
  // implicitly[TC[Child]] // doesn't compile
}

{
  implicit val inst: TC[Child] = null
  implicitly[TC[Parent]] // compiles
}

If a type class is contravariant then all subtypes of an instance of this type class are also its instances

trait TC1[-A]

{
  implicit val inst: TC1[Parent] = null
  implicitly[TC1[Child]] // compiles
}

{
  implicit val inst: TC1[Child] = null
  // implicitly[TC1[Parent]] // doesn't compile
}

Clearly, for invariant type classes there is no such property.

What is the shortest change I can do to make it compile?

It should not compile.

UPDATE 1: I just realised that the annotation "String" is the culprit, the following code total worked

Surely it does. You defined implicit of type a.type so implicit of this type a.type is found.

If you are looking for implicits of supertypes you can do

def implicitSupertypeOf[A] = new PartiallyAppliedImplicitSupertypeOf[A]
class PartiallyAppliedImplicitSupertypeOf[A] {
  def apply[B]()(implicit b: B, ev: A <:< B): B = b
  // by the way, the following will not work: 
  //    def apply[B]()(implicit ev: A <:< B, b: B): B = b
  //    def apply[B >: A]()(implicit b: B): B = b  
}

import Predef.{$conforms => _, _}

{
  implicit val p: Parent = null
  implicitSupertypeOf[Child]() //compiles
}

{
  implicit val inst: TC[Parent] = null
  implicitSupertypeOf[TC[Child]]() //compiles
}

{
  implicit val inst: TC1[Child] = null
  implicitSupertypeOf[TC1[Parent]]() //compiles
}

{
  implicit val a: String = "abc"
  implicitSupertypeOf[a.type]() //compiles
  implicitSupertypeOf["abc"]() //compiles
}

From the above it follows that there is no sense to define implicitSubtypeOf[A]() because it should behave just like standard implicitly[A] .

By the way, we can also modify behavior of implicitly so that it will accept only exact type without subtypes

def implicitExactTypeOf[A] = new PartiallyAppliedImplicitExactTypeOf[A]
class PartiallyAppliedImplicitExactTypeOf[A] {
  def apply[B <: A]()(implicit b: B, ev: A =:= B) = b
}

{
  implicit val p: Parent = null
  // implicitExactTypeOf[Child]() // doesn't compile
  implicitExactTypeOf[Parent]() // compiles
}

{
  implicit val c: Child = null
  implicitExactTypeOf[Child]() // compiles
  // implicitExactTypeOf[Parent]() // doesn't compile
}

{
  implicit val inst: TC[Parent] = null
  // implicitExactTypeOf[TC[Child]]() // doesn't compile
  implicitExactTypeOf[TC[Parent]]() //compiles
}

{
  implicit val inst: TC1[Child] = null
  implicitExactTypeOf[TC1[Child]]() //compiles
  // implicitExactTypeOf[TC1[Parent]]() // doesn't compile
}

{
  implicit val a: String = "abc"
  implicitExactTypeOf[String]() // compiles
  // implicitExactTypeOf["abc"]() // doesn't compile
  // implicitExactTypeOf[a.type]() // doesn't compile
}

Also we can implement implicitStrictSupertypeOf (accepting supertypes but not the type itself), implicitStrictSubtypeOf (like implicitly accepting subtypes but it will not accept the type itself).


Actually after discussion with @HTNW I guess I understood your point. So we just should say that compiler doesn't like to summon singletons.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM