Generic inheritance in Scala - scala

I'm trying to implement a few structures from Okasaki's book in Scala, and in tests try to keep the actual tests in the base class, only using subclasses to provide the instance-under-test.
For example, a test for unbalanced (tree) set looks as follows:
class UnbalancedSetSpec
extends SetSpec(new UnbalancedSet[Int])
with IntElements
where
abstract class SetSpec[E, S](val set: Set[E, S]) extends Specification with ScalaCheck {
implicit def elements: Arbitrary[E]
// ...
private def setFrom(es: Seq[E]): S = es.foldRight(set.empty)(set.insert)
}
Now sometimes I want to specialise the child spec, e.g.
class RedBlackSetSpec
extends SetSpec(new RedBlackSet[Int])
with IntElements {
"fromOrdList" should {
"be balanced" ! prop { (a: List[Int]) =>
val s = RedBlackSet.fromOrdList(a.sorted)
set.isValid(s) should beTrue
}
}
}
it fails because there's no method isValid on Set[E, S] — it's defined in RedBlackSet[E]. But if I go ahead and change SetSpec[E, S](val set: Set[E, S]) to SetSpec[E, S, SES <: Set[E, S]](val set: SES), this particular problem disappears, but the code still fails to compile:
Error:(7, 11) inferred type arguments [Nothing,Nothing,okasaki.RedBlackSet[Int]] do not conform to class SetSpec's type parameter bounds [E,S,SES <: okasaki.Set[E,S]]
extends SetSpec(new RedBlackSet[Int])
^
Error:(7, 11) inferred type arguments [Nothing,Nothing,okasaki.UnbalancedSet[Int]] do not conform to class SetSpec's type parameter bounds [E,S,SES <: okasaki.Set[E,S]]
extends SetSpec(new UnbalancedSet[Int])
^
The definition of RedBlackSet is as follows:
package okasaki
class RedBlackSet[E](implicit ord: Ordering[E]) extends Set[E, RBTree[E]] {
so I would expect E to be inferred as Int rather than Nothing, and S as RBTree[Int] — but it doesn't happen.
class RedBlackSetSpec
extends SetSpec[Int, RedBlackSet.RBTree[Int], RedBlackSet[Int]](new RedBlackSet[Int])
with IntElements {
and
class UnbalancedSetSpec
extends SetSpec[Int, BinaryTree[Int], UnbalancedSet[Int]](new UnbalancedSet[Int])
with IntElements
work fine, but look ugly.
I'm struggling to understand why E and S are not inferred here. Any hints?

This is actually a well-known problem with Scala type inference: it can't infer SES "first" and use it to infer E and S. One solution comes to mind:
class RedBlackSetSpec(override val set: RedBlackSet[Int]) extends SetSpec(set) with IntElements {
def this() = this(new RedBlackSet[Int])
...
}
It becomes less ugly if you make set in SetSpec an abstract val instead of a constructor argument, but with a slight tradeoff in cases you don't need to specialize. I think there should be a better one, but this should work.

Related

illegal cyclic reference involving type T

I was trying to implement a similar structure with an idea to have a common trait to work with different Input instances regardless of their InType.
trait AnyInput {
type InType
val obj : InType
}
abstract class Input[T](obj: T) extends AnyInput {
type InType = T
}
case class InpImage(image: ByteStream) extends Input[ByteStream](image)
case class InpString(text: String) extends Input[String](text)
.
.
.
trait InputProcessor[T <: Input[T#InType]] {
...
}
and I get the "cyclic reference involving type T error" in the InputProcessor definition
It is important to notice, that there might be a couple different case class implementing Input[String] or Input[ByteStream]. So writing it out as
case class StringInput(s: String) extends Input[String](s)
case class IntInput(numb: Int) extends Input[Int](numb)
is not the best workaround
Maybe you can use
trait InputProcessor[S, T <: Input[S]] {
// ...
}
you can try something like this
trait InputProcessor[S <: AnyInput, T <: Input[S#InType]] {
implicit val evidence: T =:= S = implicitly[T =:= S]
}
and than the implementation would be:
class ImageProcessor extends InputProcessor[InpImage, InpImage] { }
you can read more about =:= here: https://stackoverflow.com/a/3427759/245024
EDIT:
added an example and the implementation to the evidence
notice that when extending InputProcessor, if you pass 2 different type parameters, the code won't compile (as you would expect)

Scala: expression's type is not compatible with formal parameter type

trait Ingredient{}
case class Papperoni() extends Ingredient{}
case class Mushroom() extends Ingredient{}
trait ToppingDef[T] {
}
object PepperoniDef extends Serializable with ToppingDef[Papperoni] {
}
object MushroomDef extends Serializable with ToppingDef[Mushroom] {
}
class Oven[T <: Ingredient](val topping:ToppingDef[T]) {
}
class Pizza {
def cook = {
val topping =
if(someCondition()) { PepperoniDef }
else { MushroomDef}
new Oven(topping) // <-- build error here
}
}
I am using Scala 2.11. This example is somewhat contrived but I’ve stripped out everything unrelated to the problem to provide a concise example.
The error I get on the last line is:
Error:(26, 5) no type parameters for constructor Oven: (topping: ToppingDef[T])Oven[T] exist so that it can be applied to arguments (Serializable with ToppingDef[_ >: Papperoni with Mushroom <: Product with Serializable with Ingredient])
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Serializable with ToppingDef[_ >: Papperoni with Mushroom <: Product with Serializable with Ingredient]
required: ToppingDef[?T]
new Oven(topping)
However changing the last line to this for example:
new Oven(PepperoniDef)
builds fine. So the compiler has no problem finding the type when the parameter is passed explicitly like this.
Also, removing the Serializable trait from PepperoniDef and MushroomDef like this:
object PepperoniDef extends ToppingDef[Papperoni] {
}
object MushroomDef extends ToppingDef[Mushroom] {
}
also builds. However in my case I need the Serializable.
I think I can probably restructure the code to work around this if necessary but I would like to understand what's going on, I don't know why the type is ambiguous in the first case, or why the presence of the Serializable trait has any effect. Thanks in advance for any insights.
EDIT: Thank you for the replies, very helpful. I think the most concise fix is to change this:
val topping =
to this:
val topping:ToppingDef[_ <: Ingredient] =
Which cures the build error and does not require a change to the generic classes, which I would like to keep as simple an unannotated as possible so as to have Scala infer as much type information as possible.
This doesn't answer the question of why the presence of Serializable has any effect on this.
It seems that helping the compiler out with type annotation makes this compile:
val topping: ToppingDef[
_ >: Papperoni with Mushroom <: Ingredient with Product with Serializable] =
if (true) {
PepperoniDef
} else {
MushroomDef
}
I don't think this has to do with the Serializable class specifically, it seems like a compiler quirk to me since the produced type has a mixed in type including Product with Serializable anyway.
You can also "relax" the type signature by making T covariant, meaning Topping[Ingredient] will be inferred. This happens because the "is subtype of" relation Papperoni <: Ingredient on a covariant ToppingDef[+T] means ToppingDef[Papperoni] <: ToppingDef[Ingredient], thus allowing the common supertype for T:
trait ToppingDef[+T]
val topping: ToppingDef[Ingredient with Product with Serializable] =
if (true) {
PepperoniDef
} else {
MushroomDef
}
And this also compiles without type annotation.
Edit:
Making Ovens type parameter an existential instead of a universally quantified type seems to work as well with the Serializable trait:
class Oven[_ <: Ingredient](val topping: ToppingDef[_])
val topping: Serializable with ToppingDef[_ <: Ingredient] =
if (true) {
PepperoniDef
} else {
MushroomDef
}
new Oven(topping)
You need to add some type information to your trait:
trait ToppingDef[+T <: Ingredient] {}
Right now the topping variable is not able to figure out that T is supposed to be an Ingredient so you need to tell it.

Scala typing: "Expected _$1, got T"

I have a Lift project with mixed Java-Scala code. The project has a JPA backend written in Java with EclipseLink, which is accessed by the Scala side, which uses Lift and Lift-NG.
On the Java side, I have the following relevant interfaces:
interface IEntity
interface IDAO<T extends IEntity> {
void persist(T t);
}
On the Scala side, I have the following:
abstract class Binding[T <: IEntity] extends NgModel {
def unbind: T
}
class BasicService[B <: Binding[_ <: IEntity]](serviceName: String, dataAccessObject: IDAO[_ <: IEntity]) {
def persist(binding : B): Unit = {
val entity = binding.unbind
dataAccessObject.persist(entity)
}
}
The purpose of this hierarchy is to let me create Service instances which handle a given Binding for entity E, which can also receive and use an appropriate DAO created to handle type E. For example:
// Java side
class Todo implements IEntity
class TodoManager implements IDAO<Todo>
// Scala side
case class TodoBinding extends Binding[Todo] {
override def unbind: Todo = new Todo()
}
object HelloWorld extends BasicService[TodoBinding]("todoService", new TodoManager)
My problem is a compilation error that occurs inside the persist method of BasicService. On the last line, I get this:
Type mismatch: expected _$1, actual: IEntity
As I am a bit inexperienced with Scala, I might be missing something very obvious with the type system, but I genuinely cannot figure this out. How can I work around this error?
Your definition of BasicService does not require that the type argument of B:
B <: Binding[_ <: IEntity]
is compatible with the type argument of dataAccessObject:
dataAccessObject: IDAO[_ <: IEntity]
What if one is Binding[FooEntity] and the other is IDAO[BarEntity]?
You should take that type variable E that you say you are trying to use, and actually define it:
class BasicService[E <: IEntity, B <: Binding[E]]
(serviceName: String, dataAccessObject: IDAO[E])
Also note that in the example code you posted, the type variable B to BasicService might not be necessary, because you only use it to receive the argument binding to persist(). You could have simply used the type Binding[E] there:
class BasicService[E <: IEntity]
(serviceName: String, dataAccessObject: IDAO[E]) {
def persist(binding: Binding[E]): Unit = // ...
}

underscore "class needs to be abstract since method is not defined" error

With scala 2.10.2, I have a code that extends from class scala.swing.ListView[SomeClass] and I get this error:
src/some.scala:100: error: class Some_Renderer needs to be abstract, since method componentFor in class Renderer of type (list: scala.swing.ListView[_ <: SomeClass], isSelected: Boolean, focused: Boolean, a: SomeClass, index: Int)scala.swing.Component is not defined
private class Some_Renderer extends ListView.Renderer[SomeClass]
EDIT: "an extra Note" is actually the error here, as commented B[String] must be B[_].
EDIT: some real example follows
I have reproduced it as the following example, though with an extra Note:
scala> class B[A] { val m : Int = 0 }
defined class B
scala> abstract class C[A] { def f(l: B[_]):Int }
defined class C
scala> private class D extends C[String] { def f(l: B[String]):Int = { l.m } }
<console>:9: error: class D needs to be abstract, since method f in class C of type (l: B[_])Int is not defined
(Note that B[_] does not match B[String]: their type parameters differ)
private class D extends C[String] { def f(l: B[String]):Int = { l.m } }
EDIT: MWE
import scala.swing.{Component, Label, ListView}
class Test
object Retn extends Label { opaque = false }
private class TestRenderer extends ListView.Renderer[Test]
{
def componentFor(list: ListView[_], isSelected: Boolean, focused: Boolean, test: Test, index: Int): Component =
{
val retn = Retn
retn
}
}
<console>:9: error: class TestRenderer needs to be abstract, since method componentFor in class Renderer of type (list: scala.swing.ListView[_ <: Test], isSelected: Boolean, focused: Boolean, a: Test, index: Int)scala.swing.Component is not defined
private class TestRenderer extends ListView.Renderer[Test]
^
EDIT: MWE
I am only trying to patch that line of code, and do scala programming by no means.
Yet I do not understand what is the problem with this example. I found similar questions
but not with this underscore ("_") thing.
The example tries to provide an implementation for the abstract method f from class C[A].
Thank s for your comments or answers about this issue.
B[_] is an "existential type", but you can understand this as being roughly equivalent to B[Any].
The error message tells you Note that B[_] does not match B[String]: their type parameters differ. You wouldn't expect to be able to replace a List[Any] parameter with a List[String] in an overridden definition, would you? Because then what happens when you pass it a List[Int], which is also a bona fide List[Any] as required in the abstract class?
Solution: replace def f(l: B[String]):Int = ... with def f(l: B[_]):Int = ...
Edit: the problem with your MWE is that you've defined the TestRenderer class on one line, then you've written a code block delimited by { } on the next... this will be interpreted as you intend in normal code (although it's better to put the { at the end of the previous line) but since you're in the REPL the class definition is interpreted as soon as you hit return. Solution: either put the { at the end of the private class ... line or type in :paste to allow you to write the lot before it's interpreted.
... at least, I assume that's what your problem is. The MWE works fine for me in scala 2.10.0 REPL, although I get a slightly different error message if I just type in the private class ... line.
I'm not sure what the constraints are in your original case, but in your example, could you use B[_] instead of B[String] when you override f?
class D extends C[String] { def f(l: B[_]):Int = { l.m } }
The underscore in this case is an existential type, and can can be any subtype of Any. See here for more information on existential types in Scala.

Scala : generics and variable inheritance issue

I have a curious problem, maybe because my classes' structure is a bit complex, but anyway :
So first I have 2 abstract classes : TestAbstract1 and TestAbstract2.
TestAbstract2 takes a type extending TestAbstract1
TestAbstract1 declares a val named valTest of type TestAbstract2[TestAbstract1] that has to be implemented in child classes
Code :
abstract class TestAbstract1 {
val valTest: TestAbstract2[TestAbstract1]
def meth1(): List[TestAbstract1] = {
valTest.meth2()
}
}
abstract class TestAbstract2[T <: TestAbstract1] {
def meth2(): List[T] = {
List()
}
}
Then I have one object TestObject2 that extends TestAbstract2, and a basic class Test2 that extends TestAbstract1, and has to implement valTest :
class Test2 extends TestAbstract1 {
val valTest: TestAbstract2[Test2] = TestObject2
}
object TestObject2 extends TestAbstract2[Test2] { }
The problem is here : when I compile, it tells me :
[error] overriding value valTest in class TestAbstract1 of type models.test.TestAbstract2[models.test.TestAbstract1];
[error] value valTest has incompatible type
[error] val valTest: TestAbstract2[Test2] = TestObject2
I don't know what I am doing wrong, because if I think about polymorphism rules, it should be alright ...
Do you have any idea ? Or maybe even a better way of doing what I want ?
Thank you !
In your example, TestAbstract2 is not covariant. Meaning that even if we have
Test2 <: TestAbstract1
It is not the case that:
TestAbstract2[Test2] <: TestAbstract2[TestAbstract1]
Have a look here if this does not make sense to you.
In your example, valTest as declared in Test2 is of type TestAbstract2[Test2] but expected to be TestAbstract2[TestAbstract1], therefore the error.
You have the following options:
Declare TestAbstract2 as covariant:
class TestAbstract2[+T <: TestAbstract1]
Declare valTest using wildchard types:
val valTest: TestAbstract2[_ <: TestAbstract1]
Parametrize TestAbstract1 on the type of the inner TestAbstract2:
class TestAbstract1[T <: TestAbstract1[T]] {
val valTest: TestAbstract2[T]
// ...
}
Changing Test2 to:
class Test2 extends TestAbstract1[Test2]
Note that the choice of using F-bounded polymorphism (bounding T by a function of itself in TestAbstract1) in the third example is somewhat arbitrary here. I just needed some type to put in for the sake of the example, and in your example it works (when looking at the definition of Test2). Which of these three versions is the best for you depends on how you want to use these classes.
If this is not sufficient for you, please provide more detail in your questions and we'll gladly help.