Why doesn't Scala fully infer type parameters when type parameters are nested?

后端 未结 3 1738
傲寒
傲寒 2020-12-31 08:46

Consider the following Scala code:

abstract class A
abstract class B[T <: A]
class ConcreteA extends A
class ConcreteB extends B[ConcreteA]

class Example         


        
相关标签:
3条回答
  • 2020-12-31 09:03

    (See also two related questions: Scala fails to infer the right type arguments and Type infered to Nothing in Scala)

    It looks like a limitation of Scala's type inference, which is intentionally not spec'ed. As work-around, you can get inference by making T a type member of B rather than parameter,

    abstract class A
    abstract class B { type T <: A }
    class ConcreteA extends A
    class ConcreteB extends B { type T = ConcreteA }
    class Example[U <: B]( resolver: U )
    object Test {
        new Example( new ConcreteB )
    }
    

    When using type members, it's useful to know that they can be surfaced as type parameters using refinement, as in Miles Sabin's answer to: Why is this cyclic reference with a type projection illegal?

    In Jean-Philippe Pellet's answer to a related question, type inference was aided by making the type parameter higher kinded. If you introduce an extra type parameter in ConcreteB, then type inference can work,

    abstract class A
    abstract class B[T <: A]
    class ConcreteA extends A
    class ConcreteB[T <: A] extends B[T]
    class Example[T <: A, U[T0 <: A] <: B[T0]]( resolver: U[T] )
    object Test {
      new Example( new ConcreteB[ConcreteA] )
    }
    

    Scala 2.9 gives the mysterious error message below, but Miles Sabin points out it is a bug that will be fixed for 2.9.1

    <console>:15: error: kinds of the type arguments (ConcreteA,ConcreteB[T0]) do not conform to the expected kinds of the type parameters (type T,type U) in class Example.
    ConcreteB[T0]'s type parameters do not match type U's expected parameters: class ConcreteB has one type parameter, but type U has one
             new Example( new ConcreteB[ConcreteA] )
                 ^
    
    0 讨论(0)
  • 2020-12-31 09:07

    Kipton got close with his higher-kinded solution. Unfortunately he tripped over what appears to be a bug in Scala < 2.9.1.RC1. The following works as expected with 2.9.1.RC1 and trunk,

    Welcome to Scala version 2.9.1.RC1 (Java HotSpot(TM) Server VM, Java 1.7.0).
    Type in expressions to have them evaluated.
    Type :help for more information.
    
    scala> abstract class A
    defined class A
    
    scala> abstract class B[T <: A]
    defined class B
    
    scala> class ConcreteA extends A
    defined class ConcreteA
    
    scala> class ConcreteB[T <: A] extends B[T]
    defined class ConcreteB
    
    scala> class Example[T <: A, U[X <: A] <: B[X]](resolver: U[T])
    defined class Example
    
    scala> new Example(new ConcreteB[ConcreteA])
    res0: Example[ConcreteA,ConcreteB] = Example@ec48e7
    
    0 讨论(0)
  • 2020-12-31 09:11

    I have composed a document of type inference workarounds on GitHub for my own learning.

    A few simple rules that I find useful are:

    • Type parameters of type parameters cannot be inferred: Scala type inference only sees types specified in the parameter list (not to be confused with type parameter list).

    • Previous parameters are not used to infer future parameters: Type information only flows across parameter lists, not parameters.


    However, in this particular example type members are the way forward (thanks @Kipton Barros!)

    0 讨论(0)
提交回复
热议问题