I am using Scala 2.10-RC5
Here is my code:
object Fbound {
abstract class E[A <: E[A]] {
self: A =>
def move(a: A): Int
}
class A extends E[A] {
override def toString = "A"
def move(a: A) = 1
}
class B extends E[B] {
override def toString = "B"
def move(b: B) = 2
}
def main(args: Array[String]): Unit = {
val a = new A
val b = new B
val l = List(a, b)
val t = l.map(item => item.move(null.asInstanceOf[Nothing]))
println(t)
}
}
when run the program, exception occurs:
Exception in thread "main" java.lang.NullPointerException
at fb.Fbound$$anonfun$1.apply(Fbound.scala:20)
at fb.Fbound$$anonfun$1.apply(Fbound.scala:20)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.immutable.List.foreach(List.scala:309)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
at scala.collection.AbstractTraversable.map(Traversable.scala:105)
at fb.Fbound$.main(Fbound.scala:20)
at fb.Fbound.main(Fbound.scala)
my question is:
Why it pass the compiler but fail at runtime?
Adding a line at the bottom - val t1 = l.map(item => item.move(item)) will fail the compiler, why?
Your code with null.asInstanceOf[Nothing] compiles because Nothing is a subclass of everything and, as such, complies with the required type for move. Needless to say, it will throw an exception at runtime.
When you try to compile the second line you gave, you are given something in the lines of this error:
<console>:19: error: type mismatch;
found : E[_6(in value $anonfun)] where type _6(in value $anonfun) >: B with A
<: E[_ >: B with A <: Object]
required: <root>._6
When you put the two instances in the same List, you have lost important information about the type of their elements. The compiler can't ensure that an element of type T >: B with A <: E[_ >: B with A] can be passed to the move method of an object of the same type, the same way that you can't do:
val c: E[_ >: B with A] = new A
val d: E[_ >: B with A] = new B
c.move(d) // note: the _ in c and d declarations are different types!
I don't know enough about self-types to be completely sure of this explanation, but it seems to me that it is a class-level restriction, and not an instance-level one. In other words, if you lose the information about the type parameter in E, you can't expect the compiler to know about the move argument particular type.
For instance-level restrictions, you have this.type. If you define move as:
def move(a: this.type): Int
Your code compiles, but I don't think it is what you want, as move will accept only the same instance you are calling it on, which is useless.
I can't think of any way you may enforce that restriction the way you want. I suggest you try to do that with type variables (i.e. defining a type variable type T = A in class E), which have, as far as I know, some degree of binding to instances. Perhaps you can explain in more detail your specific situation?
Re: second question
The problem is type of the argument of move method, I can't see it being possible to make it work if it is in any way tied to subclass(es), instead:
l has to be 'some form' of list of E-s
so then item will be 'some form' of E
type of argument to move has to have the same type
This is then the only way I could make it work with type arguments (changing btw name of type argument to X, not to be confused with name of class A, and removing self type declaration, which I think is irrelevant for this problem/discussion):
abstract class E[X <: E[X]]
{
def move (a: E[_]): Int
}
class A extends E[A]
{
def move(a: E[_]): Int = 1
}
class B extends E[B]
{
def move(b: E[_]): Int = 2
}
...
{
val a = new A
val b = new B
val l = List (a, b)
val t = l.map (item => item.move(item))
...
}
If someone could give a more 'type-restrictive' solution, I would really like to see it.
Another option, as Rui has suggested, would be to use a type member, something like this:
abstract class E
{
type C <: E
def move (x: E): Int
}
class A extends E
{
type C = A
def move(x: E): Int = 1
}
class B extends E
{
type C = B
def move(x: E): Int = 2
}
...
{
val a = new A
val b = new B
val l = List (a, b)
val t = l.map (item => item.move(item))
...
}
Related
I am trying to have a method return a value that must implement a typeclass, and I think it is highlighting to me more generally how I don't understand Scala's generic parameter resolution process. I have a situation something like this:
trait IsContainer[A, T] {
def getOtherContainer[O](implicit tc2: IsUnit[O]): O
}
trait IsUnit[A] { }
implicit val anIntIsUnit = new IsUnit[Int] { }
implicit def aListIsContainer[T] = new IsContainer[List[T], T] {
def getOtherContainer[Int] = 3
}
This is raising a compile error: Missing implementation for: def getOtherContainer. My uninformed guess about what should be happening here is that Scala sees I have passed the generic parameter O, and considers the method implemented if all instances of the O type are consistent. So in this case (since I have explicitly told it that O = Int, it checks that there is an instance of IsUnit[Int] in scope, and that the output type of the method is of type O. If this is correct (and I'm not saying it is!) then why is this not working?
More generally, if I skipped the [O] generic parameter and let it guess - so I just implemented the method with getOtherContainer = 3 - should I also expect it work? To infer what O should be, does it scan over the line and see if O has been concretely filled out in any of the three places it is mentioned, and infer from that?
Thanks!
Correct is
implicit def aListIsContainer[T] = new IsContainer[List[T], T] {
override def getOtherContainer[O](implicit tc2: IsUnit[O]): O = ???
}
Your type class
trait IsContainer[A, T] {
def getOtherContainer[O](implicit tc2: IsUnit[O]): O
}
means that if a tuple of types A, T have an instance of the type class then you know how to do getOtherContainer for any type O having an instance of type class IsUnit.
When you're trying to remove (implicit tc2: IsUnit[O]) or [O] in the definition of an instance you're actually trying to violate the contract of type class.
If you want to specialize O in an instance (for example O := Int) then you should move type parameter O to the type class level
trait IsContainer[A, T, O] {
def getOtherContainer(implicit tc2: IsUnit[O]): O
}
or
abstract class IsContainer[A, T, O](implicit tc2: IsUnit[O]) {
def getOtherContainer: O
}
or
trait IsContainer[A, T] {
type O
def getOtherContainer(implicit tc2: IsUnit[O]): O
}
Then you can define an instance
implicit def aListIsContainer[T] = new IsContainer[List[T], T, Int] {
override def getOtherContainer(implicit tc2: IsUnit[Int]): Int = 3
}
or
implicit def aListIsContainer[T] = new IsContainer[List[T], T, Int] {
override def getOtherContainer: Int = 3
}
or
implicit def aListIsContainer[T] = new IsContainer[List[T], T] {
override type O = Int
override def getOtherContainer(implicit tc2: IsUnit[O]): O = 3
}
correspondingly.
The main problem is this definition:
def getOtherContainer[Int] = 3
In this definition, Int is a type parameter, not the type Int. It is exactly the same as
def getOtherContainer[T] = 3
So you have not "explicitly told it that O = Int", you have just used Int as the name of type parameter rather than O. Since this does not match the required signature in the trait, there is no valid implementation of getOtherContainer and you get the error.
I am currently learning scala and i am confused about the variance annotations especially the covariance and contravariance.
So i did some research an came upon following example
class Box[+T] {
def put[U >: T](x: U): List[U] = {
List(x)
}
}
class Animal {
}
class Cat extends Animal {
}
class Dog extends Animal {
}
var sb: Box[Animal] = new Box[Cat];
So this says class Box is covariant in T whiche means Box[Cat] is a subclass of Box[Animal] since Cat is a subclass of Animal. Sofar i understand this. But when it comes to method parameters my understanding ends. The spec says methods parameters can not be covariant so we have to use this lower bound annotation.
Lets look at the method definiton
def put[U >: T](x: U): List[U] = {
List(x)
}
So [U >: T] says that U must be a superclass of T
Trying following code
var sb: Box[Animal] = new Box[Cat];
sb.put(new Cat);
Works as expected but this drives me nuts
var sb: Box[Animal] = new Box[Cat];
sb.put(1);
It logically makes no sense to me to put an INT into a Box of Animals alltough its correct since INT will be resolved to Any which is a superclass of Animal.
So my question is
How do i have to adapt the code that the put method accepts only subtypes of
animal? I can not use the upper bound annotation
class Box[+T] {
def put[U <: T](x: U): List[U] = {
List(x)
}
}
since i get this well known error
covariant type T occurs in contravariant position in type
You can add both a lower and an upper bound:
class Box[+T] { def put[U >: T <: Animal](x: U): List[U] = List(x) }
But this is not what you want, since you wire the definition of Box to Animal and logically there is no reason to add such an upper bound.
You say:
It logically makes no sense to me to put an INT into a Box of Animals alltough its correct since INT will be resolved to Any which is a superclass of Animal.
You don't put an Int into a Box[Animal], the existing box is immutable (and it is not possible to make it mutable, since the definition of covariance doesn't allow it). Instead you get a box (or in case of your put method) of a new type. If your goal is to get only a List[Anmial], then you just have to specify that:
scala> class Box[+T] { def put[U >: T](x: U): List[U] = List(x) }
defined class Box
scala> var b: Box[Animal] = new Box[Cat]
b: Box[Animal] = Box#23041911
scala> val xs: List[Animal] = b put new Dog
xs: List[Animal] = List(Dog#f8d6ec4)
scala> val xs: List[Animal] = b put 1
<console>:14: error: type mismatch;
found : Int(1)
required: Animal
val xs: List[Animal] = b put 1
^
scala> val xs = b put 1 // this will result in a List[Any]
xs: List[Any] = List(1)
There is no need to complicate the definition of the put method.
For a more in depth explanation about why co- and contravariance is needed see this question.
I'm working on a small library for economic models that check the Units of the entities, using Types, e.g. instead of val apples = 2.0 we write val apples = GoodsAmount[KG, Apples](2.0). For creating bundle of goods, I trying to use HLists from the shapeless library. This works fine, but in some cases I can not be as generic code as I prefer. See e.g. the following problem.
I start with a simple code that explain what I want to lift into shapeless. We create two classes, on that represent Km, the other Miles. It should be allowed to add Km classes, but not miles. That I use a abstract type T is mainly motivated be our more complex library. And the indirect call to the '+' function is just because we need something similar in the shapeless case behind.
trait Foo {
type T
val v: Double
def +[B <: Foo](other: B)(implicit ev: this.T =:= other.T) = v + other.v
}
trait _Km
trait _Miles
case class Km(v: Double) extends Foo { type T = _Km }
case class Miles(v: Double) extends Foo { type T = _Miles }
object ExampleSimple extends App {
def add[A <: Foo, B <: Foo](a: A, b: B)(implicit ev: a.T =:= b.T) = { a + b }
add(Km(1), Km(2))
// add(Km(1), Miles(2)) /* does not compile as intended */
}
This works as intended. But it's necessary to have the Type Contraint check on the 'add' function. My attempt to extend this to HLists looks like this:
object ExampleShapeless extends App {
import shapeless._
val l1 = Km(1) :: Km(2) :: HNil
val l2 = Km(4) :: Km(3) :: HNil
object add extends Poly1 {
implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a, b) => a + b }
}
(l1 zip l2).map(add)
}
But this generate the following error message (using Scala 2.10.2):
[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:50: Cannot prove that a.T =:= b.T.
[error] implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a: Foo, b) => a + b }
[error] ^
[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:54: could not find implicit value for parameter mapper: shapeless.Mapper[ExampleShapeless.add.type,shapeless.::[(Km, Km),shapeless.::[(Km, Km),shapeless.HNil]]]
[error] (l1 zip l2).map(add)
The first error should be fixed, in the case that I could add a Type Constraint to the caseTuple function, but to be honest, I have not understood how the at function is working and where I could add the implicit evidence parameter. And I'm also don't know, what I must do, so that the Mapper would find his implicit value.
A less generic version, where I replase the caseTuple function with
implicit def caseTuple = at[(Km,Km)] { case (a, b) => a + b }
works fine, but would need to write a lot of redundant code (okay, this solution would be still better as our current solution using Tuples). Can somebody give me a hint how I can solve this problem?
Thanks,
Klinke
You can require the type members to match by adding a type parameter to the case:
object add extends Poly1 {
implicit def caseTuple[_T, A <: Foo { type T = _T }] = at[(A, A)] {
case (a, b) => a + b
}
}
Or you could use an existential type, since you only really care that they're the same:
object add extends Poly1 {
implicit def caseTuple[A <: Foo { type T = _T } forSome { type _T }] =
at[(A, A)] {
case (a, b) => a + b
}
}
Either version will provide the behavior you want.
Ok, so I have this:
implicit final class RichIterableLike[A, Repr <: IterableLike[A, Repr]](val it: Repr)
extends AnyVal {
def pairDiff[To](implicit num: Numeric[A], cbf: CanBuildFrom[Repr, A, To]): To = {
val b = cbf(it)
val iter = it.iterator
if (iter.hasNext) {
var pred = iter.next()
while (iter.hasNext) {
import num.mkNumericOps
val succ = iter.next()
b += succ - pred
pred = succ
}
}
b.result()
}
}
This compiles, but doesn't kick in:
val stabs = IndexedSeq(1.0, 2.0, 3.0)
stabs.pairDiff
Gives: value pairDiff is not a member of IndexedSeq[Double]
Explicit conversion works:
new RichIterableLike[Double, IndexedSeq[Double]](stabs).pairDiff
... how to fix this?
EDIT
If I apply the approach of this answer, it works:
implicit final class RichIterableLike[A, CC[~] <: Iterable[~]](val it: CC[A])
extends AnyVal {
def pairDiff[To](implicit num: Numeric[A], cbf: CanBuildFrom[CC[A], A, To]): To = {
...
}
But the question remains, what is the crucial difference that makes the implicit lookup kick in in the latter case.
In order for the implicit lookup to work it needs a link between A and Repr (IterableLike demands that link). You pass it in as an argument, so that argument should be typed as Repr[A]. That means you need to modify your signature so it will look something like this:
RichIterableLike[A, Repr[X] <: IterableLike[X, Repr[X]]](val it: Repr[A])
With the above signature you say:
I have an object that accepts a type parameter, I will name that object Repr and when you pass it in I would like to capture the type parameter as well. I will name that type parameter A. As an extra condition I want the type of Repr to conform to the signature of IterableLike
The question about Bidirectional Links in Traits with a Single Type, focused on having a reference to this when both the parents and children are the same type.
self is an excellent solution for situations where the parent and children are the same type ... but what if the parent and children are different types as in the code at the end of the question?
The error on the commented line Error rightly complains that:
type mismatch; found :PolyTree.this.type (with underlying type T) required: C
which makes total sense because self is defined as T.
The goal is to be able to write:
val parentOrder = new ParentOrder
val childOrder = new ChildOrder
childOrder.addParent(parentOrder)
where parentOrder is added to childOrder's parents and childOrder is added to parentOrder's children.
Any idea how to have to structure the code to remove the error and be able to write code like is shown above?
trait PolyTree[T <: PolyTree[T, C], C <: PolyTree[T, C]] { self: T =>
private val _parents: ListBuffer[T] = ListBuffer()
private val _children: ListBuffer[C] = ListBuffer()
def addParent(parent: T): PolyTree[T, C] = {
_parents += parent
parent._children += this // Error
this
}
def addChild(child: C): PolyTree[T, C] = {
_children += child
child._parents += this
this
}
}
Basically the same solution, you need both T and C as self type so :
trait PolyTree[T <: PolyTree[T, C], C <: PolyTree[T, C]] { self: T with C => ...