I am trying to extend class with overloaded method so I can instantiate it and pass different number of argument but keep common type of Foo and Bar. How can I make my code runnable?
class GenericClass {
def run(x: Int): Unit
def run(x: Int, y: Int): Unit
}
class Foo extends GenericClass {
def run(x: Int): Unit
}
class Bar extends GenericClass {
def run(x: Int, y: Int)
}
def getRun(hello: String) = {
hello match {
case "foo" => new Foo
case "bar" => new Bar
}
}
def execIt(runType: String) = {
case "foo" => getRun(runType).run(2)
case "bar" => getRun(runType).run(2, 3)
}
execIt("foo")
You can try to introduce a type member
trait GenericClass {
type In
def run(x: In): Unit
}
class Foo extends GenericClass {
override type In = Int
override def run(x: Int): Unit = println("Foo#run")
}
class Bar extends GenericClass {
override type In = (Int, Int)
override def run(x: (Int, Int)): Unit = println("Bar#run")
}
def getRun(hello: String) = {
hello match {
case "foo" => new Foo
case "bar" => new Bar
}
}
def execIt(runType: String) = getRun(runType) match {
case foo: Foo => foo.run(2)
case bar: Bar => bar.run(2, 3)
}
execIt("foo") // Foo#run
or a type class
trait GenericClass
class Foo extends GenericClass
class Bar extends GenericClass
trait Run[A <: GenericClass, T] {
def run(a: A, t: T): Unit
}
object Run {
implicit val foo: Run[Foo, Int] = (a, t) => println("Run.foo")
implicit val bar: Run[Bar, (Int, Int)] = (a, t) => println("Run.bar")
}
implicit class RunOps[A <: GenericClass](val a: A) extends AnyVal {
def run[T](t: T)(implicit r: Run[A, T]): Unit = r.run(a, t)
}
def getRun(hello: String) = {
hello match {
case "foo" => new Foo
case "bar" => new Bar
}
}
def execIt(runType: String) = getRun(runType) match {
case foo: Foo => foo.run(2)
case bar: Bar => bar.run(2, 3)
}
execIt("foo") // Run.foo
Please notice that I modified execIt as well.
Also you can keep everything as is and just throw NotImplementedError for methods that aren't needed
trait GenericClass {
def run(x: Int): Unit
def run(x: Int, y: Int): Unit
}
class Foo extends GenericClass {
def run(x: Int): Unit = println("Foo#run")
def run(x: Int, y: Int): Unit = ???
}
class Bar extends GenericClass {
def run(x: Int): Unit = ???
def run(x: Int, y: Int) = println("Bar#run")
}
def getRun(hello: String) = {
hello match {
case "foo" => new Foo
case "bar" => new Bar
}
}
def execIt(runType: String) = runType match {
case "foo" => getRun(runType).run(2)
case "bar" => getRun(runType).run(2, 3)
}
execIt("foo") // Foo#run
although this last option seems to abuse OOP.
Related
I have some overloaded methods that take in multiple types and return the same type:
def foo(x: Int): Foo = ...
def foo(x: String): Foo = ...
def foo(x: Boolean): Foo = ...
def foo(x: Long): Foo = ...
Now I want to define a single way to call the method, something like:
def bar(x: Int | String | Boolean | Long) = foo(x) // how to do this?
I can do it the "naive" way which I don't like very much:
def bar(x: Any) = x match {
case i:Int => foo(i)
case s:String => foo(s)
case b:Boolean => foo(b)
case l:Long => foo(l)
case _ => throw new Exception("Unsupported type")
}
Is there a better way, perhaps using Scalaz or some other library?
Try type class
trait FooDoer[T] {
def foo(x: T): Foo
}
object FooDoer {
implicit val int: FooDoer[Int] = (x: Int) => foo(x)
implicit val string: FooDoer[String] = (x: String) => foo(x)
implicit val boolean: FooDoer[Boolean] = (x: Boolean) => foo(x)
implicit val long: FooDoer[Long] = (x: Long) => foo(x)
}
def bar[T](x: T)(implicit fooDoer: FooDoer[T]): Foo = fooDoer.foo(x)
bar(1)
bar("a")
bar(true)
bar(1L)
// bar(1.0) // doesn't compile
Also sometimes the following can help
def bar[T](x: T)(implicit ev: (T =:= Int) | (T =:= String) | (T =:= Boolean) | (T =:= Long)) = ???
trait |[A, B]
trait LowPriority_| {
implicit def a[A, B](implicit a: A): A | B = null
}
object | extends LowPriority_| {
implicit def b[A, B](implicit b: B): A | B = null
}
How to define "type disjunction" (union types)?
A typeclass might work like this:
trait CanFoo[T] {
def foo(t: T): Foo
}
object CanFoo {
implicit object intFoo extends CanFoo[Int] {
def foo(i: Int) = Foo(i)
}
implicit object stringFoo extends CanFoo[String] {
def foo(s: String) = Foo(s)
}
implicit object boolFoo extends CanFoo[Boolean] {
def foo(i: Boolean) = Foo(i)
}
implicit object longFoo extends CanFoo[Long] {
def foo(i: Long) = Foo(i)
}
}
def bar[T](x: T)(implicit ev: CanFoo[T]) =
ev.foo(x)
bar(0)
bar("hello")
bar(true)
bar(0.toLong)
Having the following code:
import shapeless.{::, Generic, HList, HNil, Lazy}
object Problem {
trait In {
def bar: Double
}
trait A {
def foo: Int
type I <: In
}
/////////////////////////////////////////////////////
final case class A1In(d: Double) extends In {
override def bar: Double = 1.1 + d
}
final case class A1() extends A {
override def foo: Int = 1
override type I = A1In
}
final case class A2In(d: Double) extends In {
override def bar: Double = 1.1 + d
}
final case class A2() extends A {
override def foo: Int = 1
override type I = A2In
}
final case class AListIn[T <: HList](items: T)(implicit ev: isIn[T]) extends In {
override def bar = 1.1
}
final case class AList[T <: HList](items: T)(implicit ev: isA[T]) extends A {
override def foo: Int = 555
override type I = AListIn[???]
}
trait isA[T] {
def aux_foo(value: T): Int
}
trait isIn[T] {
def aux_bar(value: T): Double
}
/////////////////////////////////////////////////////
def alloc(a: A): In = ????
def usage() = {
val a1: A1 = A1()
val a2: A2 = A2()
val l: AList[::[A1, ::[A2, ::[A1, HNil]]]] = AList(a1 :: a2 :: a1 :: HNil)
val a1In: A1In = A1In(1.2)
val a2In: A2In = A2In(9.3)
val lIn: AListIn[::[A2In, ::[A1In, HNil]]] = AListIn(a2In :: a1In :: HNil)
}
}
How can I fix it so it works as expected?
E.g how do I get correct type in place of ??? which is a proper type HList being result of applying isA -> isIn type mapping. The mapping must follow the natural association of A -> In mapping defined as type I <: In in trait A
And how to implement alloc function which for any concrete instance of In will produce corresponding instance of A?
Should concrete implementations of In be path dependent types of corresponding As?
Below is the code for isA the code for isIn is analogous
trait isA[T] {
def aux_foo(value: T): Int
}
object isA {
// "Summoner" method
def apply[T](implicit enc: isA[T]): isA[T] = enc
// "Constructor" method
def instance[T](func: T => Int): isA[T] = new isA[T] {
override def aux_foo(value: T): Int = func(value)
}
implicit def a1Encoder: isA[A1] = instance(i => 4)
implicit def a2Encoder: isA[A2] = instance(i => 9)
implicit def hnilEncoder: isA[HNil] = instance(hnil => 0)
implicit def hlistEncoder[H, T <: HList](implicit
hInstance: Lazy[isA[H]],
tInstance: isA[T]
): isA[H :: T] = instance {
case h :: t => hInstance.value.aux_foo(h) + tInstance.aux_foo(t)
}
implicit def genericInstance[A, R](implicit
generic: Generic.Aux[A, R],
rInstance: Lazy[isA[R]]
): isA[A] = instance { value => rInstance.value.aux_foo(generic.to(value)) }
}
I have two methods to add elements to a customized collection:
class WrappedMap[A, B] {
protected var a2b = Map[A, B]()
def +=(a: A, b: B) = {
a2b += a -> b
this
}
def +=(t: (A,B)): this.type = {
this.+=(t._1, t._2)
}
def ++=(t: Iterable[(A,B)]) = {
(this /: t){ case (b, elem) => b += elem }
}
}
val c = new WrappedMap[Int, Int]
c ++= Seq((1, 2),(2, 4))
It throws a Stackoverflow exception because the call to the += inside ++= calls the +=, which in turns call itself instead of calling the first variant.
How can I change the call in the body of the second += so that it calls the first one?
The problem is the specification of this.type as the return type from the second form of +=. Not sure the exact reason but the compiler does not recognise the first form as returning this.type therefore it can't be used to satisfy the return type of the second form. I recreated it with:
case class Foo[A,B](s: String) {
def bar(a: String, b: String) = {
this.copy(s"a: $a b: $b")
}
def bar(c: (String, String)): this.type = {
this.bar (c._1, c._2)
}
}
val f = Foo("")
f bar (("hello", "world"))
Instead, you can just use the class and type params as the return type:
case class Foo[A, B](s: String) {
def bar(a: String, b: String) : Foo[A,B] = {
this.copy(s"a: $a b: $b")
}
def bar(c: (String, String)): Foo[A, B] = {
this.bar (c._1, c._2)
}
}
val f = Foo[Int, Long]("")
f bar (("hello", "world"))
Or, where inheritance needs to be supported:
class Foo[A, B] {
private var s: String = ""
def bar(a: String, b: String) : this.type = {
s = s"a: $a b: $b"
this
}
def bar(c: (String, String)): this.type = {
this.bar (c._1, c._2)
}
}
case object Foo2 extends Foo[Int, Long]
Foo2 bar (("hello", "world"))
I have following question: having typeclass for ADT derived with LabelledTypeClassCompanion (where both product and coproduct are correctly defined) inside Scala object, why compiler can't find instance for datatype itself (coproduct), but is able to do so for parameters of specific data constructors (product) when called inside the same object and assigned to val?
Below is a snippet for Show typeclass which is mostly borrowed from corresponding Shapeless example:
import shapeless._
object ShowGeneric {
trait Show[T] {
def show(t: T): String
}
object Show extends LabelledTypeClassCompanion[Show] {
implicit def intShow: Show[Int] = new Show[Int] {
override def show(i: Int): String = i.toString
}
implicit def booleanShow: Show[Boolean] = new Show[Boolean] {
override def show(b: Boolean): String = b.toString
}
implicit def listShow[A](implicit showA: Show[A]): Show[List[A]] = new Show[List[A]] {
override def show(l: List[A]): String = l.map(showA.show).mkString("List(", ", ", ")")
}
object typeClass extends LabelledTypeClass[Show] {
override def emptyProduct: Show[HNil] = new Show[HNil] {
override def show(t: HNil): String = ""
}
override def product[H, T <: HList](name: String, sh: Show[H], st: Show[T]): Show[H :: T] = new Show[H :: T] {
override def show(t: H :: T): String = {
val head = s"$name = ${sh.show(t.head)}"
val tail = st.show(t.tail)
if(tail.isEmpty) head else s"$head, $tail"
}
}
override def coproduct[L, R <: Coproduct](name: String, cl: => Show[L], cr: => Show[R]): Show[L :+: R] = new Show[L :+: R] {
override def show(t: L :+: R): String = t match {
case Inl(l) => s"$name(${cl.show(l)})"
case Inr(r) => cr.show(r)
}
}
override def emptyCoproduct: Show[CNil] = new Show[CNil] {
override def show(t: CNil): String = ""
}
override def project[F, G](instance: => Show[G], to: F => G, from: G => F): Show[F] = new Show[F] {
override def show(t: F): String = instance.show(to(t))
}
}
}
implicit class ShowOps[T](t: T)(implicit showT: Show[T]) {
def show: String = showT.show(t)
}
sealed trait Whatever
case class IntBool(i: Int, b: Boolean) extends Whatever
case class BoolInt(b: Boolean, i: Int) extends Whatever
case class BoolListInt(b: Boolean, l: List[Int]) extends Whatever
def showWhatever(whatever: Whatever)(implicit showWhatever: Show[Whatever]): String = {
showWhatever.show(whatever)
}
val stringBoolInt = BoolInt(false, 100).show // Compiles
val stringShowWhatever = showWhatever(BoolInt(false, 100)) // Doesn't compile: could not find implicit value for parameter showWhatever:ShowGeneric.Show[ShowGeneric.Whatever]
}
Thanks in advance for your answers!
Given simplified code example:
sealed trait A {
val c1: String
val c2: Int
def copy[Z <: A](src: File) : Z
}
case class B(c1: String, c2: Int, src: File) extends A
case class C(c1: String, c2: Int, c3: MyClass, src: File) extends A
how do I define copy method in trait A so it will match generated one for case class and 'target' file? Given definition does typecheck and complains about missing method copy in classes B and C.
scala compiler will not generate copy methods for case class that defines method with name copy.
scala -Xprint:typer -e "sealed trait A { def copy[T <: A](s: String):T }; case class B(x: Int, y:Int) extends A"
outputs:
/var/folders/fm/fm4b21vj6jl995ywlrd99t49tjthjc/T/scalacmd3413244935208502669.scala:1: error: class B needs to be abstract, since method copy in trait A of type [T <: this.A](s: String)T is not defined
sealed trait A { def copy[T <: A](s: String):T }; case class B(x: Int, y: Int) extends A
^
one error found
[[syntax trees at end of typer]] // scalacmd3413244935208502669.scala
package <empty> {
object Main extends scala.AnyRef {
def <init>(): Main.type = {
Main.super.<init>();
()
};
def main(argv: Array[String]): Unit = {
val args: Array[String] = argv;
{
final class $anon extends scala.AnyRef {
def <init>(): anonymous class $anon = {
$anon.super.<init>();
()
};
sealed abstract trait A extends scala.AnyRef {
def copy[T >: Nothing <: this.A](s: String): T
};
case class B extends AnyRef with this.A with Product with Serializable {
<caseaccessor> <paramaccessor> private[this] val x: Int = _;
<stable> <caseaccessor> <accessor> <paramaccessor> def x: Int = B.this.x;
<caseaccessor> <paramaccessor> private[this] val y: Int = _;
<stable> <caseaccessor> <accessor> <paramaccessor> def y: Int = B.this.y;
def <init>(x: Int, y: Int): this.B = {
B.super.<init>();
()
};
override <synthetic> def productPrefix: String = "B";
<synthetic> def productArity: Int = 2;
<synthetic> def productElement(x$1: Int): Any = x$1 match {
case 0 => B.this.x
case 1 => B.this.y
case _ => throw new IndexOutOfBoundsException(x$1.toString())
};
override <synthetic> def productIterator: Iterator[Any] = runtime.this.ScalaRunTime.typedProductIterator[Any](B.this);
<synthetic> def canEqual(x$1: Any): Boolean = x$1.$isInstanceOf[this.B]();
override <synthetic> def hashCode(): Int = {
<synthetic> var acc: Int = -889275714;
acc = Statics.this.mix(acc, x);
acc = Statics.this.mix(acc, y);
Statics.this.finalizeHash(acc, 2)
};
override <synthetic> def toString(): String = ScalaRunTime.this._toString(B.this);
override <synthetic> def equals(x$1: Any): Boolean = B.this.eq(x$1.asInstanceOf[Object]).||(x$1 match {
case (_: this.B) => true
case _ => false
}.&&({
<synthetic> val B$1: this.B = x$1.asInstanceOf[this.B];
B.this.x.==(B$1.x).&&(B.this.y.==(B$1.y)).&&(B$1.canEqual(B.this))
}))
};
<synthetic> private object B extends scala.runtime.AbstractFunction2[Int,Int,this.B] with Serializable {
def <init>(): this.B.type = {
B.super.<init>();
()
};
final override <synthetic> def toString(): String = "B";
case <synthetic> def apply(x: Int, y: Int): this.B = new B(x, y);
case <synthetic> def unapply(x$0: this.B): Option[(Int, Int)] = if (x$0.==(null))
scala.this.None
else
Some.apply[(Int, Int)](Tuple2.apply[Int, Int](x$0.x, x$0.y));
<synthetic> private def readResolve(): Object = $anon.this.B
}
};
{
new $anon();
()
}
}
}
}
}