15

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.

0__
  • 66,707
  • 21
  • 171
  • 266
Mysterious Dan
  • 1,316
  • 10
  • 25
  • Err, this might be a duplicate of the (still unanswered) http://stackoverflow.com/questions/18557472/f-bounded-polymorphism-with-abstract-types – Mysterious Dan May 05 '14 at 21:29

1 Answers1

19

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
leogrim
  • 306
  • 2
  • 4
  • 2
    I didn't know this was at all possible! Using a lower bound and a self type is ingenious! – Daniel C. Sobral May 05 '14 at 22:16
  • That's interesting, but the two subclasses of `Event` still don't seem to unify to just `Event`, which was my ultimate goal. – Mysterious Dan May 05 '14 at 22:30
  • It seems like `Product with Serializable with Event{type E >: UserLeft with UserJoined <: Product with Serializable with Event` is a finer LUB than `Event`, which has to do with the way abstract type members are handled by the compiler. In your original example, I believe the compiler first resolves all types and then fills in implicit parameters accordingly, so it fails to find your implicit value as TC is invariant in A. – leogrim May 05 '14 at 23:13
  • Ah, that's unfortunate. In practice, my `TC` is `Ordering`, which is also invariant and I of course can't change :( – Mysterious Dan May 05 '14 at 23:53
  • Not sure you would want to use a contra-variant type in this situation anyway. It's probably better to force the compiler to infer `Foo` using an intermediate `val` and an explicit type declaration. That may also make the code and typing assumptions clearer for you to read it later :) – leogrim May 06 '14 at 06:20
  • I'm mostly just frustrated that I can't have "a case class whose members can talk about themselves but otherwise behaves just like a regular case class" :( sure, I can annotate things, but I don't want every user of this type in my system to have a terrifying type (my actual use case is more complicated) just one failed unification away. – Mysterious Dan May 06 '14 at 14:21