Here's a very simple example:
scala> class A(x: Int) {
| def withX(newX: Int): A = new A(newX)
| override def toString = x.toString
| }
defined class A
scala> class B(x: Int) extends A(x) {
| override def withX(newX: Int): B = new B(newX)
| }
defined class B
So we have two classes:
A, which defines a method withX returning the object of type A, and
B, which extends A and overrides its withX method to return the object of type B.
Now I'd like to create a container, which can process instances of both A and B. It should have a method, which can accept an instance of A (or B), run withX on it and return another instance of A (or B). To be more specific, the exact functionality I want to achieve (note the return types):
scala> val a = new A(0)
a: A = 0
scala> val b = new B(0)
b: B = 0
scala> val testA = new Test[A]
testA: Test[A] = Test#4bf59a3d
scala> val testB = new Test[B]
testB: Test[B] = Test#65e565d1
scala> testA.test(a)
res1: A = 1
scala> testB.test(b)
res2: B = 1
I suppose that this can be achieved via class with upper-bounded type parameter. I've tried to do it like this, but got an error:
scala> class Test[T <: A] {
| def test(t: T): T = t withX 1
| }
<console>:9: error: type mismatch;
found : A
required: T
def test(t: T): T = t withX 1
^
The only workaround I managed to find looks somewhat ugly. Besides that, the match clause will need to be rewritten if we add some new class C extends B:
scala> class Test[T <: A] {
| def test(t: T): T = (t match {
| case b: B => b withX 1
| case a: A => a withX 1
| }).asInstanceOf[T]
| }
Can this functionality be achieved in more elegant way? I feel like I'm missing something about type parameters, type bounds and all scala type system in general.
While it's possible to override a method with a more specific return type, it's rarely a good idea. Consider the following:
class C(x: Int) extends A(x) {
override def withX(newX: Int): B = new B(x)
}
This is completely legal, but breaks your assumption that the return type of withX is the same subtype of A as the (statically known) type of the instance you're calling it on.
You can make that assumption safely (i.e. with support from the type system) by using F-bounded polymorphism:
trait Base[T <: Base[T]] {
def withX(newX: Int): T
}
class A(x: Int) extends Base[A] {
def withX(newX: Int) = new A(newX)
override def toString = x.toString
}
class B(x: Int) extends Base[B] {
def withX(newX: Int) = new B(newX)
override def toString = x.toString
}
class Test[T <: Base[T]] {
def test(t: T): T = t withX 1
}
This requires some changes to your type hierarchy, but it's a pretty standard way to encode the idea that the return type of a method is the same as the instance it's called on.
Somewhat along the lines of Travis' answer, if you want to encode the fact that a type supports a specific operation (withX in this case), you can consider using a type class.
class A(x: Int) {
override def toString = x.toString
}
class B(x: Int) extends A(x)
trait HasWithX[T] {
def withX(x: Int): T
}
implicit val AHasWithX = new HasWithX[A] {
def withX(x: Int) = new A(x)
}
implicit val BHasWithX = new HasWithX[B] {
def withX(x: Int) = new B(x)
}
class Test[T] {
def test(t: T)(implicit ev: HasWithX[T]): T = ev withX 1
}
Example:
scala> val a = new A(0)
a: A = 0
scala> val b = new B(0)
b: B = 0
scala> val testA = new Test[A]
testA: Test[A] = Test#4bf59a3d
scala> val testB = new Test[B]
testB: Test[B] = Test#65e565d1
scala> testA.test(a)
res1: A = 1
scala> testB.test(b)
res2: B = 1
Related
I want to define a method in a Scala trait where both a parameter to the method and the return type correspond to the same concrete class which extends the trait. I've tried something like the following:
trait A {
def foo(obj: this.type): this.type
}
final case class B(val bar: Int) extends A {
override def foo(obj: B): B = {
B(obj.bar + this.bar)
}
}
object Main {
def main(args: Array[String]) = {
val b1 = new B(0)
val b2 = new B(0)
val b3: B = b1.foo(b2)
}
}
However, trying to compile this code gives the following error:
Test.scala:5: error: class B needs to be abstract. Missing implementation for:
def foo(obj: B.this.type): B.this.type // inherited from trait A
case class B(val bar: Int) extends A {
^
Test.scala:6: error: method foo overrides nothing.
Note: the super classes of class B contain the following, non final members named foo:
def foo: ((obj: _1.type): _1.type) forSome { val _1: B }
override def foo(obj: B): B = {
^
2 errors
There's obviously something I'm misunderstanding about the Scala type system here. The signature of foo in class B is what I want it to be, but I don't know how to correctly define the method in A (or if this is even possible). It seems like this question is asking something quite similar, but I don't immediately see how the answer applies in my situation.
The type annotation this.type means that you may only return this. So in that case you may not return another instance of B, the same holds for the method parameter.
If this was just about the return type, a solution would be to require foo to return something of type A, the override method in B can specialize the return type to return B.
However since you also have an argument which you want to be of the type of the subtype you could use a Self Recursive Type. The following example compiles and should do what you want.
trait A[S <: A[S]] {
def foo(obj: S): S
}
case class B(val bar: Int) extends A[B] {
override def foo(obj: B): B = {
B(obj.bar + 1)
}
}
Consider type class solution
case class B(bar: Int)
// type class
trait Fooable[A] {
def foo(x: A, y: A): A
}
// proof that B satisfies Fooable constraints
implicit val fooableB: Fooable[B] = new Fooable[B] {
override def foo(x: B, y: B): B = B(x.bar + y.bar)
}
// a bit of syntax sugar to enable x foo y
implicit class FooableOps[A](x: A) {
def foo(y: A)(implicit ev: Fooable[A]) = ev.foo(x,y)
}
val b1 = B(1)
val b2 = B(41)
b1.foo(b2)
// B(42)
which Scala 3 simplifies to
case class B(bar: Int)
// type class
trait Fooable[A] {
extension (x: A) def foo (y: A): A
}
// proof that B satisfies Fooable constraints + syntactic sugar
given Fooable[B] with
extension (x: B) def foo (y: B): B = B(x.bar + y.bar)
val b1 = B(1)
val b2 = B(41)
b1.foo(b2)
// B(42)
See Scala FAQ: How can a method in a superclass return a value of the “current” type?
I'd like to use abstract type and type refinement to encode something like a functional dependency between two types.
trait BaseA {
type M
type P <: BaseB.Aux[M]
def f(m: M): Unit
}
trait BaseB {
type M
def m(): M
}
object BaseB {
type Aux[M0] = BaseB { type M = M0 }
}
It means that a A only works with a B if they have the same type M inside.
With the following concret classes
class A extends BaseA {
type M = Int
type P = B
def f(m: Int): Unit = {
println("a")
}
}
class B extends BaseB {
type M = Int
def m(): M = 1
}
So now they have both Int type as M, but the following code does not compile
val a: BaseA = new A
val b: BaseB = new B
def f[T <: BaseA](a: T, b: T#P): Unit = {
a.f(b.m())
}
The compiler tells me that a.f here expect a path dependent type a.M but it got a b.M.
My question here is how can I express the fact that I only need the M type matched in the type level, not the instance level? Or how can I prevent the M in def f(m: M): Unit becoming a path-dependent type?
Thanks!
I think the issue comes from b being related to a type T, and it being possible for a to be a subclass of T that could override M to be something else, making the two objects incompatible. For instance:
class A2 extends BaseA {
type M = String
type P = B2
def f(s: String): Unit = {
println(s)
}
}
class B2 extends BaseB {
type M = String
def m(): M = "foo"
}
val a: BaseA = new A
val b: BaseB = new B2
f[BaseA](a, b)
It seems like if your f were to compile, then all of this should compile too.
You can either make b's type dependent on a.P:
def f(a: BaseA)(b: a.P): Unit
Or I think the whole thing is simplified by not having the compatible types restriction on your classes, but rather require that one is a subclass of the other at the point that they interact:
trait BaseA {
type M
def f(m: M): Unit
}
trait BaseB {
type M
def m(): M
}
class A extends BaseA {
type M = Int
def f(m: Int): Unit = {
println("a")
}
}
class B extends BaseB {
type M = Int
def m(): M = 1
}
val a: A = new A
val b: B = new B
def f(a: BaseA, b: BaseB)(implicit sub: b.M <:< a.M): Unit = {
a.f(sub(b.m()))
}
f(a, b)
Or lastly, consider whether you need these to be path-dependent types at all; could they be regular generic type parameters?
trait BaseA[-M] {
def f(m: M): Unit
}
trait BaseB[+M] {
def m(): M
}
class A extends BaseA[Int] {
def f(m: Int): Unit = {
println("a")
}
}
class B extends BaseB[Int] {
def m(): Int = 1
}
def f[T](a: BaseA[T], b: BaseB[T]): Unit = {
a.f(b.m())
}
I got the following issue when trying to use typeclasses throughout my project.
trait FooAble[T] { def fa(t: T): List[T] }
object Foo { def apply[T](t: T) = implicitly[FooAble[T]].fa(t) }
trait BarAble[T] { def fb(t: T): Double }
object Bar { def apply[T](t: T) = implicitly[BarAble[T]].fb(t) }
And would like to be able to do the following:
// xs contains elements of type A and B which are subclasses of the trait Something
def f(xs: List[Something]) = {
val something = xs.map(Foo)
val somethingElse = xs.map(Bar)
}
However, this would not work as we don't know if Something implements A[]and B[], no implicit implementation found. What do I need to do so that the elements of the list xs implement the typeclasses FooAble and BarAble?
I think this question: What are type classes in Scala useful for? will help you to understand the proper use (& usefulness) of type classes.
Am just extending the answer by Kevin Wright in the above link for your use case (if I understand your need correctly!):
trait Addable[T] {
def zero: T
def append(a: T, b: T): T
}
trait Productable[T] {
def zero: T
def product(a: T, b: T): T
}
implicit object IntIsAddable extends Addable[Int] {
def zero = 0
def append(a: Int, b: Int) = a + b
}
implicit object IntIsProductable extends Productable[Int] {
def zero = 1
def product(a: Int, b: Int) = a*b
}
def sumAndProduct[T](xs: List[T])(implicit addable: Addable[T], productable: Productable[T]) =
(xs.foldLeft(addable.zero)(addable.append), xs.foldLeft(productable.zero)(productable.product))
So akin to above, in your use case, you need to provide implicit objects which implement your type classes FooAble & BarAble and your method signature for function f becomes:
def f[Something](xs: List[Something])(implicit fooable: FooAble[Something], barable: BarAble[Something])
to make it short, this works:
object Main {
def main(args: Array[String]) {
trait T1[T] {
def f1(a: T): Double
}
val ea1 = new T1[List[String]] {
def f1(a: List[String]): Double = a.length
}
}
}
But this won't compile:
object Main {
def main(args: Array[String]) {
trait T1 {
def f1[T](a: T): Double
}
val ea1 = new T1 {
def f1(a: List[String]): Double = a.length
}
}
}
object creation impossible, since method f1 in trait T1 of type [T](a: T)Double is not defined
val ea1 = new T1 {
^
It seems like the method is not taken into account because of type parameter on method.
How can I achieve this without using trait type parameters or trait abstract types?!
TIA!
You may want to define a type T to do away with Trait Type Parameters and accomplish the same as..
trait T1 {
type T
def f1(a: T): Double
}
val ea1 = new T1 {
type T = List[String]
def f1(a: T): Double = a.length
}
ea1.f1(List("1","2"))
// res0: Double = 2.0
There is a "private option" -Yinfer-argument-types that lets you:
scala> trait T { def f(i: Int) }
defined trait T
scala> new T { def f(i) = 2 * i }
res1: T = $anon$1#7915e83
You're asking for an option that would do the equivalent of:
scala> new T { def f() = 2 * i }
except with type parameters instead of value parameters.
I'm not sure what the semantics would be in your example. The method has a type param, but throw if it's not the type I expect?
Edit: Maybe you mean this:
scala> trait T { def f[ #specialized(Int) A](a: A): A = ??? }
defined trait T
scala> new T { def f(i: Int) = 2*i }
res0: T{def f(i: Int): Int} = $anon$1#50844aeb
scala> res7 f 7
warning: there were 1 feature warning(s); re-run with -feature for details
res8: Int = 14
There is no type param on the specialized method.
Update: This is probably the first appearance of elided REPL stack traces on SO:
scala> res7 f "hi!"
warning: there were 1 feature warning(s); re-run with -feature for details
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:229)
at $anon$1.f(<console>:9)
Is it possible to do something like this in Scala:
class MyTest {
def foo[A <: String _or_ A <: Int](p:List[A]) = {}
}
That is, the type A could be a String or Int. Is this possible?
(Similar question here)
Not really possible as you put it, but you can do it using the type class pattern. For example, from here:
sealed abstract class Acceptable[T]
object Acceptable {
implicit object IntOk extends Acceptable[Int]
implicit object LongOk extends Acceptable[Long]
}
def f[T: Acceptable](t: T) = t
scala> f(1)
res0: Int = 1
scala> f(1L)
res1: Long = 1
scala> f(1.0)
<console>:8: error: could not find implicit value for parameter ev: Acceptable[Double]
f(1.0)
^
EDIT
This works if class and object are companions. On REPL, if you type each on a different line (ie, a "result" appears between them), they are not companions. You can type it like below, though:
scala> sealed abstract class Acceptable[T]; object Acceptable {
| implicit object IntOk extends Acceptable[Int]
| implicit object LongOk extends Acceptable[Long]
| }
defined class Acceptable
defined module Acceptable
You could get a little mileage from the Either type. However the Either hierarchy is sealed and handling more than two types becomes cumbersome.
scala> implicit def string2either(s: String) = Left(s)
string2either: (s: String)Left[String,Nothing]
scala> implicit def int2either(i: Int) = Right(i)
int2either: (i: Int)Right[Nothing,Int]
scala> type SorI = Either[String, Int]
defined type alias SorI
scala> def foo(a: SorI) {a match {
| case Left(v) => println("Got a "+v)
| case Right(v) => println("Got a "+v)
| }
| }
foo: (a: SorI)Unit
scala> def bar(a: List[SorI]) {
| a foreach foo
| }
bar: (a: List[SorI])Unit
scala>
scala> foo("Hello")
Got a Hello
scala> foo(10)
Got a 10
scala> bar(List(99, "beer"))
Got a 99
Got a beer
Another solution is wrapper classes:
case class IntList(l:List[Int])
case class StringList(l:List[String])
implicit def li2il(l:List[Int]) = IntList(l)
implicit def ls2sl(l:List[String]) = StringList(l)
def foo(list:IntList) = { println("Int-List " + list.l)}
def foo(list:StringList) = { println("String-List " + list.l)}
There is this hack:
implicit val x: Int = 0
def foo(a: List[Int])(implicit ignore: Int) { }
implicit val y = ""
def foo(a: List[String])(implicit ignore: String) { }
foo(1::2::Nil)
foo("a"::"b"::Nil)
See http://michid.wordpress.com/2010/06/14/working-around-type-erasure-ambiguities-scala/
And also this question.