1

I would like to have a class B that inherits from a generic class A that is parametrized by an inner type of B. Specifically, I would like this (minimized example):

class A[T]
class B extends A[T] {
  class T
}

Written like this, the compiler does not accept it. Is there any way to specify this inheritance relationship? (Using some different syntax, or some tricks.)

If not, what would be an official reference documenting that this is not possible?

Notes:

  • Yes, I want T to be an inner class. I do want separate instances of B to have incompatible types T.

  • This question considers the same situation but does not ask whether/how it is possible, but instead asks for other ways to write the same thing. (At least that's how the answers seem to interpret it.) In any case, that question is ten years old, and Scala may have evolved since.

  • Clarification: I want B to inherit from A, not from an inner class of A, or have an inner class of B inherit from A. The reason is that I want to use it in the following situation: A is a type class. B is supposed to provide a quick way to easily generate new classes of that type class (with minimal boilerplate). I want to the user of the class to do something like: implicit val X = createBInstance(), and then they will be able to use the type X.T and it will have type class A. The best I managed so far is to support the pattern val X = createBInstance(); implicit val xTypeclass = X.typeclass and use X.T. This means more boilerplate, the possibility to forget something, and it pollutes the namespace more (additional name xTypeclass). (For the very curious: A is MLValue.Converter, B is QuickConverter, and X is Header/RuntimeError/... in my code.)

Dominique Unruh
  • 1,248
  • 8
  • 23

1 Answers1

4

You can try

class A {
  type T
}
class B extends A {
  class T
}

val b: B = new B
val t: b.T = new b.T

I made T a type member rather than type parameter.

Here a class overrides a type.

If you want to use T also as a type parameter you can introduce Aux-type

object A {
  type Aux[_T] = A { type T = _T }
}

and use type A.Aux[T] as a replacement to your original A[T].


Now you're doing

trait A[X]

class B {
  class T

  /*implicit*/ object typeclass extends A[T]
}

val b = new B
implicit val b_typeclass: b.typeclass.type = b.typeclass

val b1 = new B
implicit val b1_typeclass: b1.typeclass.type = b1.typeclass

implicitly[A[b.T]]
implicitly[A[b1.T]]

Please notice that making object typeclass implicit is now useless.

Making object typeclass implicit would be useful if you made b, b1 objects rather than values

trait A[X]

class B {
  class T

  implicit object typeclass extends A[T]
}

object b extends B
object b1 extends B

implicitly[A[b.T]]
implicitly[A[b1.T]]

If you want to make B extend A then as earlier I propose to replace type parameter of A with a type member. A will still be a type class, just type-member type class rather than type-parameter type class

trait A {
  type X
}

object A {
  type Aux[_X] = A {type X = _X}
}

class B extends A {
  type X = T

  class T
}

implicit object b extends B
implicit object b1 extends B

implicitly[A.Aux[b.T]]
implicitly[A.Aux[b1.T]]
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Thanks. Unfortunately, that doesn't work because I want `B` to be an instance of `A` itself (I want `B` to be a witness for a type class `A`). I added a clarification at the end of my question. – Dominique Unruh Sep 30 '20 at 08:26
  • 1
    The second but last solution works for me (`object b extends B`). I did not expect `b.typeclass` to be considered by Scala as an implicit because `b.typeclass` is not imported. As it turns out, the [scala spec](https://www.scala-lang.org/files/archive/spec/2.13/07-implicits.html#implicit-parameters) says: "[...] eligible are also all implicit members of some object that belongs to the implicit scope of the implicit parameter's type, T." Turns out, `Header` is in the implicit scope (see the spec for a definition) of `Header.T`, so `Header.typeclass` is found. Nice! – Dominique Unruh Oct 01 '20 at 09:18