scalatype-parametertype-constraintskind-projector

Generalized constraints within type parameter clause?


SLS specifies syntax of type parameter clause as

TypeParamClause   ::=  ‘[’ VariantTypeParam {‘,’ VariantTypeParam} ‘]’
FunTypeParamClause::=  ‘[’ TypeParam {‘,’ TypeParam} ‘]’
VariantTypeParam  ::=  {Annotation} [‘+’ | ‘-’] TypeParam
TypeParam         ::=  (id | ‘_’) [TypeParamClause] [‘>:’ Type] [‘<:’ Type] {‘<%’ Type} {‘:’ Type}                  {‘<%’ Type} {‘<%’ Type}

where we see >:, <:, <%, <%, : as allowed reserved names in type parameter clause. Is there a way we could use generalised type constraint symbolic names <:<, =:= in the type parameter clause such that

def f[T =:= 42] = ???

would expand to

def f[T](implicit ev: T =:= 42) = ???

similar to how context bound

def f[T: Numeric] = ???

expands to

def f[T](implicit ev: Numeric[T]) = ???

Solution

  • In 2.13 (which supports singleton types if you are curious about constraining on singletons) you can do things like:

    @ import $plugin.$ivy.`org.typelevel:kind-projector_2.13.1:0.11.0`
    import $plugin.
    
    @ type a = 23
    defined type a
    
    @ def f[N : * =:= a]: Unit = ()
    defined function f
    
    @ f[a]
    
    
    @ f[23]
    
    
    @ f[25]
    cmd9.sc:1: Cannot prove that 25 =:= Int(23).
    val res9 = f[25]
                ^
    Compilation Failed
    
    @ def g[N : * =:= 16]: Unit = ()
    defined function g
    
    @ g[16]
    
    
    @ g[23]
    cmd11.sc:1: Cannot prove that 23 =:= 16.
    val res11 = g[23]
                 ^
    Compilation Failed
    

    So, yes, it seems possible. You just have to use kind projectors to apply second parameter.

    With <:< it should be the same story:

    @ def h[N : * <:< 16]: Unit = ()
    defined function h
    
    @ h[16]
    
    
    @ h[17]
    cmd13.sc:1: Cannot prove that 17 <:< 16.
    val res13 = h[17]
                 ^
    Compilation Failed