Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to concretely set abstract type with type bound?

Tags:

types

scala

I am trying to use case objects' types as abstract types. I was surprised to see (similar) code below compiles:

sealed abstract class Bar

case object BarOne extends Bar

case object BarTwo extends Bar

sealed abstract class Foo {
  type A <: Bar

  def f: A
}

object Foo {
  object FooOne extends Foo {
    type A = BarOne.type
    val f = BarTwo
  }

  object FooTwo extends Foo {
    type A = BarTwo.type
    val f = BarOne
  }
}

In my real example Foo is parametrized and used as a case class. So I can't just make A a type parameter.

How does f = BarTwo compile, when A is set to be BarOne.type?

If A in f: A is interpreted as A <: Bar, why is that so?

Is there a way to concretely set A for each object instance of Foo?


I am using Scala 2.11.8.


Update: when I replace val attributeType = ... with def attributeType = ... in FooOne & FooTwo compilation fails (as expected).

like image 726
muhuk Avatar asked Jul 07 '16 11:07

muhuk


2 Answers

Has anyone suggested you upgrade to a modern version of Scala? (Joke.)

The error about the override gives a nice path for the type.

$ scala
Welcome to Scala 2.12.0-M5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_92).
Type in expressions for evaluation. Or try :help.

scala> :pa
// Entering paste mode (ctrl-D to finish)

sealed abstract class Bar

case object BarOne extends Bar

case object BarTwo extends Bar

sealed abstract class Foo {
  type A <: Bar

  def f: A
}

object Foo {
  object FooOne extends Foo {
    type A = BarOne.type
    val f = BarTwo
  }

  object FooTwo extends Foo {
    type A = BarTwo.type
    val f = BarOne
  }
}

// Exiting paste mode, now interpreting.

<console>:26: error: overriding method f in class Foo of type => Foo.FooOne.A;
 value f has incompatible type
           val f = BarTwo
               ^
<console>:31: error: overriding method f in class Foo of type => Foo.FooTwo.A;
 value f has incompatible type
           val f = BarOne
               ^

The bug was this one, and the duplicate question is from last November.

The nature of the bug was also clever: it was introduced by -Yoverride-objects, which is not a very useful option but figures in a couple of my S.O. answers, and now in a question.

Edit:

$ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_92).
Type in expressions for evaluation. Or try :help.

scala> object X ; object Y
defined object X
defined object Y

scala> class C { def f: X.type = X }
defined class C

scala> class D extends C { override def f: Y.type = Y }
defined class D

scala> :quit
$ scalam
Welcome to Scala 2.12.0-M5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_92).
Type in expressions for evaluation. Or try :help.

scala> scala> object X ; object Y

// Detected repl transcript. Paste more, or ctrl-D to finish.

defined object X
defined object Y

scala> class C { def f: X.type = X }
defined class C

scala> class D extends C { override def f: Y.type = Y }
defined class D
// Replaying 3 commands from transcript.

scala> object X ; object Y
defined object X
defined object Y

scala> class C { def f: X.type = X }
defined class C

scala> class D extends C { override def f: Y.type = Y }
<console>:13: error: overriding method f in class C of type => X.type;
 method f has incompatible type
       class D extends C { override def f: Y.type = Y }
                                        ^
like image 82
som-snytt Avatar answered Nov 08 '22 09:11

som-snytt


I dont't know what is going on here, but I did get the problem a bit more isolated. Also, it works with Foo subclasses as well as objects. I've confirmed this compiles on scalac 2.11.8:

object BarOne
object BarTwo

abstract class Foo[A] {
  def attributeType: A
}

object FooContainer {
  class FooOne extends Foo[BarOne.type] {
    val attributeType = BarTwo
  }

  object FooTwo extends Foo[BarOne.type] {
    val attributeType = BarOne
  }
}
like image 1
robot1208 Avatar answered Nov 08 '22 08:11

robot1208