dtech
dtech

Reputation: 14060

Collections of F-bound polymorphic objects

Say I have an F-bounded polymorphic trait:

sealed trait FBound[Me <: FBound[Me]]
case class A() extends FBound[A]
case class B() extends FBound[B]

How can I use it if I have a collection that can be any instance?

val canBeEither: Option[FBound[_]] = Some(B())

// error: type arguments [_$1] do not conform to trait FBound's type parameter bounds [Me <: FBound[Me]]
canBeEither.collect({ case b: B => b}).foreach(b => println("got a B!"))

Upvotes: 1

Views: 77

Answers (2)

Andrey Tyukin
Andrey Tyukin

Reputation: 44918

That would be

val canBeEither: Option[X forSome { type X <: FBound[X] }] = Some(B())

but I'd urge you to think twice before using this in your code. It will also give you a bunch of warnings about existential types, which you would have to silence by importing scala.language.existentials.

Upvotes: 3

HTNW
HTNW

Reputation: 29193

You would use this type

T forSome { type T <: FBound[T] }
// equivalent
FBound[T] forSome { type T <: FBound[T] }

To represent "some FBound object". In your case

val canBeEither: Option[T forSome { type T <: FBound[T] }] = Some(B())
canBeEither.collect { case b: B => b }.foreach(println)

You probably should always avoid pulling the binder out of the type constructor application. E.g. for List:

val eithers: List[T forSome { type T <: FBound[T] }]
  = List[T forSome { type <: FBound[T] }](A(), B()) // ok
val eithers: List[T] forSome { type T <: FBound[T] }
  = List[ThereIsNothingThatCanGoHere](A(), B()) // not ok

So you may want to say

type SomeFBound = T forSome { type T <: FBound[T] }

And use SomeFBound everywhere.

Upvotes: 2

Related Questions