milessabin / shapeless

Generic programming for Scala

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

implicit summoning of records.Keys for singleton subtype of HList fail sporadically

tribbloid opened this issue · comments

Here is a simple example:

import shapeless.ops.record.Keys
import shapeless.{HList, HNil, Witness}

  import shapeless.record._
  import shapeless.syntax.singleton.mkSingletonOps

      val record = {
        ("a" ->> 1) ::
          ("b" ->> 2) ::
          HNil
      }

      def inferKeys[T <: HList](v: T)(implicit keys: shapeless.ops.record.Keys[T]) = keys

      {
        val keys = record.keys // works
        print(keys)

        inferKeys(record) // works
      }

      {
        val record2: record.type = record
        val keys = record2.keys // works
        print(keys)

        inferKeys(record2) // works
        inferKeys[record.type](record2) // compilation error!
      }

The last 2 lines are syntactically identical yet one of them can't summon the type class for Keys. Since implementation of Keys doesn't even use macro I suspect some part of the scala compiler is flawed for such case. BUT nothing is sure at the moment

Tested on scala 2.13.3.

BTW, I've printed out the type tree of [record.type] and it absolutely contains all the information for the summoning:

-+ record.type
 !-+ Int with shapeless.labelled.KeyTag[String("a"),Int] :: Int with shapeless.labelled.KeyTag[String("b"),Int] :: shapeless.HNil
   !-+   [ PARAMETER(S) ] :
   : !-+ Int with shapeless.labelled.KeyTag[String("a"),Int]
   : : !-+ shapeless.labelled.KeyTag[String("a"),Int]
   : : : !-+   [ PARAMETER(S) ] :
   : : : : !-+ String("a")
   : : : : : !-+ String .................................................................................................................. [4]
   : : : : :   !-+ CharSequence
   : : : : :   : !-- Object .................................................................................................................. [0]
   : : : : :   : !-- Any ..................................................................................................................... [1]
   : : : : :   !-+ Comparable[String]
   : : : : :   : !-+   [ PARAMETER(S) ] :
   : : : : :   :   !-- String .................................................................................................................. [4]
   : : : : :   !-- java.io.Serializable .................................................................................................... [3]
   : : : : !-- Int ..................................................................................................................... [2]
   : : : !-- Object .................................................................................................................. [0]
   : : : !-- Any ..................................................................................................................... [1]
   : : !-- Int ..................................................................................................................... [2]
   : : !-- AnyVal
   : !-+ Int with shapeless.labelled.KeyTag[String("b"),Int] :: shapeless.HNil
   :   !-+   [ PARAMETER(S) ] :
   :   : !-+ Int with shapeless.labelled.KeyTag[String("b"),Int]
   :   : : !-+ shapeless.labelled.KeyTag[String("b"),Int]
   :   : : : !-+   [ PARAMETER(S) ] :
   :   : : : : !-+ String("b")
   :   : : : : : !-- String .................................................................................................................. [4]
   :   : : : : : !-- CharSequence ............................................................................................................ [9]
   :   : : : : : !-+ Comparable[String] ...................................................................................................... [10]
   :   : : : : : : !-+   [ PARAMETER(S) ] :
   :   : : : : : :   !-- String .................................................................................................................. [4]
   :   : : : : : !-- java.io.Serializable .................................................................................................... [3]
   :   : : : : : !-- Object .................................................................................................................. [0]
   :   : : : : : !-- Any ..................................................................................................................... [1]
   :   : : : : !-- Int ..................................................................................................................... [2]
   :   : : : !-- Object .................................................................................................................. [0]
   :   : : : !-- Any ..................................................................................................................... [1]
   :   : : !-- Int ..................................................................................................................... [2]
   :   : : !-- AnyVal .................................................................................................................. [8]
   :   : !-+ shapeless.HNil
   :   :   !-- shapeless.HList ......................................................................................................... [5]
   :   :   !-- java.io.Serializable .................................................................................................... [3]
   :   :   !-- Product ................................................................................................................. [6]
   :   :   !-- Equals .................................................................................................................. [7]
   :   :   !-- Object .................................................................................................................. [0]
   :   :   !-- Any ..................................................................................................................... [1]
   :   !-- shapeless.HList ......................................................................................................... [5]
   :   !-- java.io.Serializable .................................................................................................... [3]
   :   !-- Product ................................................................................................................. [6]
   :   !-- Equals .................................................................................................................. [7]
   :   !-- Object .................................................................................................................. [0]
   :   !-- Any ..................................................................................................................... [1]
   !-+ shapeless.HList
     !-+ java.io.Serializable
     : !-- Any
     !-+ Product
     : !-- Equals
     !-- Object

The last two lines are not syntactically equivalent. The inferred type is

inferKeys[FieldType["a", Int] :: FieldType["b", Int] :: HNil](record2)

It doesn't work with the singleton type (which is a subtype) because Keys is invariant.
You could use Keys[_ >: T] instead.

@joroKr21 so type parameter cannot be inferred as singleton type even if the variable is declared so? Wow

The irony is that this correctly demonstrates the invariance you described:

type RR = record.type

val record2: RR =  ...

Really? The compiler is really mangled.

Regardless, my impression so far is that: it should be a valid improvement to make Keys contravariant to the input type, what do you think?

If we apply variance it should be consistently to all type classes. But Scala 2 implicit resolution has quirks when it comes to variance, esp. contravariance. That would also be too big of a can of worms to open in Shapeless 2 I'm afraid. And since it has an easy workaround (use Keys[_ >: T] if you need contravariance) I think it's not worth the effort.