Reputation: 8396
Given the following classes:
abstract class Foo[B]
abstract class Baz[B, F <: Foo[B]] {
def get(foo: F): B
// other methods
}
I hate that I need two type parameters in Baz
when the first is redundant. I want to write something like:
abstract class Baz[F <: Foo[B]] {
def get(foo: F): B
}
Is it possible for me to reference the B type (of F) within Baz without taking multiple type parameters? This feels like it should be possible, but I can't seem to figure out the syntax.
Upvotes: 2
Views: 283
Reputation: 51713
Can you make B
a type member rather than type parameter?
abstract class Foo { type B }
abstract class Baz[F <: Foo] {
def get(foo: F): F#B
// other methods
}
Then if you need both type parameter and type member you can use Aux-pattern
abstract class Foo { type B }
// Foo.Aux[B] instead of Foo[B]
object Foo {
type Aux[B0] = Foo { type B = B0 }
}
abstract class Baz[F <: Foo] {
def get(foo: F): F#B
// other methods
}
Can you make F
higher-kinded and get
polymorphic? (Looks like sort of "tagless final" approach.)
abstract class Foo[B]
abstract class Baz[F[X] <: Foo[X]] {
def get[B](foo: F[B]): B
// other methods
}
Can you make Foo
a type class?
abstract class Foo[F] {
type B
}
object Foo {
type Aux[F, B0] = Foo[F] { type B = B0 }
def instance[F, B0]: Aux[F, B0] = new Foo[F] { type B = B0 }
//instead of class F1 extends Foo[B1]
implicit val foo1: Aux[F1, B1] = instance
}
abstract class Baz[F](implicit val foo: Foo[F]) {
def get: foo.B
// other methods
}
or
abstract class Baz[F: Foo] {
val foo: Foo[F] = implicitly
def get: foo.B
// other methods
}
Can you extract the two type parameters to a new class?
abstract class Foo[B]
abstract class Tuple {
type B
type F <: Foo[B]
}
abstract class Baz[T <: Tuple] {
def get(foo: T#F): T#B
// other methods
}
or
abstract class Baz[T <: Tuple](t: T) {
def get(foo: t.F): t.B
// other methods
}
Upvotes: 6
Reputation: 51271
I hate that I need two type parameters in Baz when the first is redundant.
There is no redundancy. If the Baz
code makes reference to 2 different types then we need to have 2 different names for them (F
and B
, or X
and Y
, or THIS
and THAT
, it doesn't really matter).
If the Baz
code only makes reference to 1 type, but that type needs to be restricted to something that Foo
has implemented then you could add that restriction:
class Baz[X](implicit ev: Foo[X]) { ...
This can be simplified as:
class Baz[X: Foo] { ...
If the types cannot be inferred, and you wish to simplify instance creation, perhaps a type member/alias can be used.
abstract class Baz[B] {
type F = Foo[B]
def get(foo: F): B
}
But it's often clearer just to write things out.
abstract class Baz[B] {
def get(foo: Foo[B]): B
}
But this, of course, eliminates the possibility of sub-types.
Upvotes: 2