scalapolymorphismf-bounded-polymorphism

Attempting to model F-bounded polymorphism as a type member in Scala


I wanted to try writing a type whose methods can be homogeneous and return values of the same type:

object SimpleTest {
  trait Foo extends Product with Serializable {
    type Self <: Foo
    def bar: Self
  }

  case class X() extends Foo {
    type Self = X
    def bar = this
  }

  case class Y() extends Foo {
    type Self = Y
    def bar = this
  }


  trait TC[A]

  implicit val tc: TC[Foo] = new TC[Foo] { }

  def tester[A: TC](x: Seq[A]) = "foo"

  // tester(Seq(X(), Y()))
}

Unfortunately, the commented-out line calling tester fails with the following error (Scala 2.10):

Error: could not find implicit value for evidence parameter of type
SimpleTest.TC[SimpleTest.Foo{type Self >: SimpleTest.Y with SimpleTest.X <: SimpleTest.Foo}]
tester(Seq(X(), Y()))
      ^

Basically, I'm confused as to why X and Y don't unify to Foo, which seems like a clear LUB for the two of them. Clearly the type member is complicating matters but its bounds appear to be respected.

At the higher level, I'm looking for a lightweight way to get the equivalent of F-bounded polymorphism without the overhead of pervasive type parameters. This mostly seems to work, but I need to add annotations that force X and Y to unify to Foo.


Solution

  • I think this is an example of what you are looking for:

    sealed trait Event { self =>
      type E >: self.type <: Event
      def instance: E = self
    }
    
    case class UserJoined() extends Event {
      type E = UserJoined
    }
    
    case class UserLeft() extends Event {
      type E = UserLeft
    }
    

    If you would like to read more, this snippet is from a recent post that covers related concepts.

    Edit: To complete the answer, it would be:

    scala> trait Foo extends Product with Serializable with Event{}
    defined trait Foo
    
    scala> case class X() extends Foo {
         |     type Self = X
         |     def bar = this
         |   }
    defined class X
    
    scala> case class Y() extends Foo {
         |     type Self = Y
         |     def bar = this
         |   }
    defined class Y
    
    scala> List(X(),Y())
    res9: List[Foo] = List(X(), Y())
    
    scala>   def tester[A: TC](x: Seq[A]) = "foo"
    tester: [A](x: Seq[A])(implicit evidence$1: TC[A])String
    
    scala>  tester(Seq(X(), Y()))
    res10: String = foo