Is there a better way to rewrite these overloaded methods to avoid the double definition issue?
def test(a: Option[Int]) {
println(a)
}
def test(a: Option[String]) {
println(a)
}
test(Some(1))
test(Some("1"))
Example -> https://scastie.scala-lang.org/3V57pKeATFSmMNnDV2xBxA
Use a polymorphic method:
def test[T](a: Option[T]): Unit = {
println(a)
}
test(Some(1))
test(Some("1"))
Related
Say I have a Scala trait that does some computation and then calls a polymorphic method on extending classes that might have a different method signature in each class:
trait GenericThing {
val vals: Map[String, Any]
def doGenericStuff(): Unit = {
println(f"do some other stuff here on ${vals}")
doSpecificStuff(vals)
}
// what should method signature be?
def doSpecificStuff(vals: Any*)
}
class SpecificThing extends GenericThing {
val vals = Map(
"count" -> 3,
"animal" -> "rabbit",
"weight" -> 9.5
)
// broken -- doesn't match superclass signature
def doSpecificStuff(count: Int, animal: String, weight: Double): Unit = {
println(f"${count} quick brown ${animal}s weigh ${weight * count} pounds")
}
}
I want the SpecificThing#doSpecificStuff method to have a proper method signature like doSpecificStuff(count: Int, animal: String, weight: Double), not just a generic one like doSpecificStuff(vals: Any*). It may have a different arity in each implementor of the trait. (If it helps, the names of the parameters could be standardized: doSpecificStuff(a: Int, b: String, c: Double)).
Is there a way, perhaps using Shapeless or something like it, to make something like this work?
Is that val vals: Map[String, Any] specifically required ?
How about parameterising the GenericThing on input contract of doSpecificStuff ?
trait GenericThing[A] {
val value: A
def doGenericStuff(): Unit = {
println(f"do some other stuff here on ${value}")
doSpecificStuff(value)
}
def doSpecificStuff(value: A)
}
Specific Implementation :
class SpecificThing1(val value: SpecificThing1.Value) extends GenericThing[SpecificThing1.Value] {
override def doSpecificStuff(value: SpecificThing1.Value): Unit = {
println(f"${value.count} quick brown ${value.animal}s weigh ${value.weight * value.count} pounds")
}
}
object SpecificThing1 {
final case class Value(count: Int, animal: String, weight: Double)
}
Usage:
val specificThing = new SpecificThing1(SpecificThing1.Value(3, "rabbit", 9.5))
specificThing.doGenericStuff()
// do some other stuff here on SpecificValueThing(3,rabbit,9.5)
// 3 quick brown rabbits weigh 28.5 pounds
You can try
import shapeless.ops.maps.FromMap
import shapeless.{::, HList, HNil}
trait GenericThing {
def vals: Map[String, Any]
type L <: HList
def doGenericStuff()(implicit fromMap: FromMap[L]): Unit = {
println(f"do some other stuff here on ${vals}")
doSpecificStuff(fromMap(vals).getOrElse(???))
}
def doSpecificStuff(vals: L): Unit
}
class SpecificThing extends GenericThing {
override val vals = Map(
"count" -> 3,
"animal" -> "rabbit",
"weight" -> 9.5
)
override type L = Int :: String :: Double :: HNil
override def doSpecificStuff(vals: L): Unit = vals match {
case count :: animal :: weight :: HNil =>
println(f"${count} quick brown ${animal}s weigh ${weight * count} pounds")
}
}
In case this point has been missed in all the noise, this pattern simple doesn't work:
trait GenericThing {
def doSpecificStuff(vals: Any*)
}
class SpecificThing extends GenericThing {
def doSpecificStuff(count: Int, animal: String, weight: Double)
}
You cannot override a generic method with a more specific method, because you can call the generic method with arguments that the specific method does not accept.
More generally, it is not a good pattern to have a base class with lots of functionality that calls overridden methods on itself. A better model is to have the generic code delegate the non-generic behaviour to another class that is injected into the implementation, either directly or using a typeclass.
I'd use path-dependent types and value function for that
trait GenericThing {
type I
def doSpecificStuff(i: I): Unit
}
class SpecificThing extends GenericThing {
type I = (Int, String, Double) // better be case class
def doSpecificStuff(tupple: (Int, String, Double)): Unit
}
This works
trait SomeTrait {
type T
def write2( s: String): T
}
case class C() extends SomeTrait {
type T = String
override def write2(s:String): T = s }
But this does not
trait SomeTrait {
def write2[T]( s: String): T
}
case class C() extends SomeTrait {
override def write2(s: String): String =s }
To my reasoning, they ..seem similar. Why specifically does the compiler give a "method does not override anything" error? IS there a way of making this work?
If you use this definition:
trait T {
def write2[T]( s: String): T
}
Consider some client using this trait. For example:
def doSomething(t: T): Unit = {
val s: String = t.write2[String]("hello")
val n: Int = t.write2[Int]("world")
val d: Double = t.write2[Double]("!")
println(s + n.toString + d.toString)
}
I don't know what the values of s, n, and d would be, but in theory that would be a perfectly valid usage of the trait, from the compiler's perspective. So in order to truly override that write2[T] method, you would have to provide valid behavior for all possible types T.
Compare that to:
trait T2 {
type T
def write2( s: String): T
}
Or even:
trait T3[T] {
def write2(s: String): T
}
Then when callers use it:
def doSomething(t: T2): Unit = {
val x = t.write2("hello") // always returns type t.T
}
def doSomething[T](t: T3[T]): Unit = {
val x = t.write2("hello") // always returns type T
}
There's only one possible type that can be returned, once you have a specific instance of that trait. So to override that method, you need only override the behavior for that one type.
Your trait definition in the second snippet does not mean that implementations of it must have a method write2 for some type T, but that it must have a method write2 which takes a type parameter T.
That means that, if you have a value v: SomeTrait, you should be able to do
val stringed: String = v.method2[String]("foo")
val inted: Int = v.method2[Int]("foo")
This compiles:
trait T {
def write2[T]( s: String): T
}
case class C() extends T {
override def write2[T](s: String): T =s.asInstanceOf[T]
}
write2 returns a T, not a String (hence the reason why your second override does not work and the necessity in the code above for the ugly cast)
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])
In Scala 2.11.5, compiling this
object Tryout {
trait A {
def method(a: Int): Boolean
}
abstract class B extends A {
def method(a: Int) = ???
}
new B {
override def method(a: Int) = true // type mismatch here
}
}
yields a "type mismatch: found Boolean, required Nothing" at the "true". If I replace the ??? with true or false, it compiles. It also compiles if I specify the result type of "method" in the abstract class.
This is not a big issue. However I'm curious whether anybody can explain why ??? is not inferred correctly as Boolean?
Scala allows you to make the return type of an inherited method more restrictive in a sub-class.
abstract class A {
def foo: Any
}
abstract class B {
def foo: Int
}
new B {
def foo = 1
}
So when you declare def method(a: Int) = ??? in B, ??? is inferred as Nothing, because the scala compiler doesn't know if you want Boolean or Nothing. This is one of the reasons why it is always a good idea to explicitly declare return types.
Return type of def method(a: Int) = ??? is actually Nothing.
scala> def method(a: Int) = ???
method: (a: Int)Nothing
Now, method in class B is overriding the method from the parent trait. You should define your claas B like this,
abstract class B extends A {
// def method is provided by trait A
}
or
abstract class B extends A {
// def method with full signature
override def method(a: Int): Boolean = ???
}
Is it possible to define constructor signature in Scala ?
abstract class A {
def this (s: String): this.type // doesn't work
def this (i: Int): this.type // doesn't work
def this (d: Double): this.type // doesn't work
}
class B(var s: String) extends A {
def this(i: Int) = {
this("int "+i.toString())
}
def this(d: Double) = {
this("double "+d.toString())
}
}
What are you trying to achieve? You can do like this:
abstract class A(i: Int)
case class B(s: String) extends A(s.toInt) {
def this(i: Int) = {
this(i.toString)
}
def this(d: Double) = {
this(d.toString)
}
}
Usage:
B("1")
new B(1)
new B(1.0)
You can't do exactly what you want, as pointed out by other answer, but one approach is to use a factory:
trait Foo {
// methods you need
}
trait FooCompanion[T <: Foo] {
// these methods replace constructors in your example
def apply(s: String): T
def apply(i: Int): T
...
}
Implementation:
class Bar(s: String) extends Foo {
...
}
object Bar extends FooCompanion[Bar] {
def apply(s: String) = new Bar(s)
...
}
and you can have methods taking FooCompanion. This pattern is used e.g. in the Scala collections library.
No that is not possible. Constructors are special: You need to write new X() instead of X(), and there is no polymorphic dispatch, e.g. you cannot do def test[A]() = new A(). So there is no scenario in which an abstract constructor would make any sense.