scalaexistential-typehigher-kinded-typesself-type

Self-type annotation for class with higher kinded type


Given:

abstract class Databases[F[_]]

How can I make this trait work:

// Marker trait signalling the database plugin supports StaticRoles
trait StaticRoles { this: Databases[_] => }

I want to ensure StaticRoles is only mixed-in in classes that also extend Databases, however I don't care about the concrete value of the type parameter F.

The code returns:

error: _$1 takes no type parameters, expected: one

Which is fair, however it returns the same error for:

trait StaticRoles { this: Databases[_[_]] => }

I've also tried:

trait StaticRoles { this: Databases[({type T[X[_]]})#T] => }

Which gives the error:

error: kinds of the type arguments (AnyRef{type T[X[_]]}#T) do not conform to the expected kinds of the type parameters (type F) in class Databases.
       AnyRef{type T[X[_]]}#T's type parameters do not match type F's expected parameters:
       type X has one type parameter, but type _ has none

Solution

  • Correct one is

    trait StaticRoles { this: (Databases[F] forSome { type F[_] }) => }
    

    Is there a shorthand for type variable 'm forSome { type m[O] <: UpperBound[O] }` in Scala?

    Not every existential type is expressible with underscores or (in this case) allowed to be expressed with underscores.

    There is also

    trait StaticRoles { this: Databases[F forSome { type F[_] }] => }
    

    different from the former but the same as

    trait StaticRoles { this: Databases[Any] => }
    

    since

    implicitly[(Databases[F forSome { type F[_] }]) =:= Databases[Any]]
    

    (Any is actually poly-kinded).

    Databases[Any] is a subtype of Databases[F] forSome { type F[_] }

    implicitly[Databases[Any] <:< (Databases[F] forSome { type F[_] })]
    

    With type projections (#) correct is

    trait StaticRoles { this: Databases[({ type F[_] })#F] => }
    

    Databases[({ type F[_] })#F] is also a subtype of Databases[F] forSome { type F[_] } (uncomparable with Databases[Any] for invariant Databases).

    Among these three types Databases[F] forSome { type F[_] }, Databases[Any], and Databases[({ type F[_] })#F] only the first one works with

    trait IO[_]
    
    class Abc extends Databases[IO] with StaticRoles // compiles
    
    //class Abc1 extends StaticRoles // doesn't compile