Self-type annotation for class with higher kinded type

276 Views Asked by At

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
2

There are 2 best solutions below

5
Dmytro Mitin On BEST ANSWER

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
0
Bunyod On

I think you should define as following:


  abstract class Databases[F[_]]

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

    def abc: String
  }

  class Abc extends Databases[IO] with StaticRoles[IO] {
    override def abc: String = ???
  }

Note: If you remove Databases[IO] and just just define like this

class Abc extends StaticRoles[IO] {...}

This won't compile. I think this is what you want to achieve