How to avoid code duplication in companion objects of subclasses - scala

I have the following pattern in my Scala code:
class A(x: Int)
object A {
def apply(x: Int, y: Int) = new A(x + y)
}
class B(x: Int) extends A(x)
object B {
def apply(x: Int, y: Int) = new B(x + y)
}
The apply methods are exactly the same, except for the Class of the object that they construct. I would like to avoid this code duplication, especially because in my real code I have several apply methods and they are much longer.
How can I achieve this? How can I remove this code duplication?
I thought about something like this:
class A(x: Int)
class B(x: Int) extends A(x)
trait C[T <: A] {
def apply(x: Int, y: Int) = new T(x + y)
}
object A extends C[A]
object B extends C[B]
It doesn't work, because T is not a class and therefore I cannot do "new T(x + y)".

In the specific case you can do something like
class A(x: Int) {
def this(x: Int, y: Int) = this(x + y)
}
class B(x: Int) extends A(x)

class Adder[T](f: Int => T) {
def apply(x: Int, y: Int) = f(x + y)
}
class A(x: Int)
object A extends Adder(new A(_))
class B(x: Int) extends A(x)
object B extends Adder(new B(_))

I would propose this solution:
class A(x: Int)
class B(x: Int) extends A(x)
trait C[+T <: A] {
def apply(x: Int, y: Int) = create(x + y)
protected def create(x: Int): T
}
object A extends C[A] {
override protected def create(x: Int) = new A(x)
}
object B extends C[B] {
override protected def create(x: Int) = new B(x)
}
Take a look at GenericCompanion class. It is part of Scala collections library and may inspire you:
http://www.scala-lang.org/api/current/index.html#scala.collection.generic.GenericCompanion

Related

Correct usage of abstract case class

I am trying to model a scenario where I'm trying to achieve immutability rather than modifying instance variables. The way I'm achieving this is as below.
case class TSS(k:Int, v:Int)
case class Combiner(a:Int,b:Int, tss: TSS) {
def func1():Option[TSS] = None
def func2():Option[TSS] = None
def modifyState(externalV: Int): Combiner = {
Combiner(this.a + 1, this.b+1, func1())
}
}
case class OneCombiner extends Combiner {
override def func1():Option[TSS] = SOME_MODIFIED_TSS
override def func2():Option[TSS] = SOME_MODIFIED_TSS
}
case class TwoCombiner extends Combiner {
override def func1():Option[TSS] = SOME_MODIFIED_TSS
override def func2():Option[TSS] = SOME_MODIFIED_TSS
}
Now when I'm trying to use the OneCombiner and TwoCombiner as below they are using the func1 and func2 of base class and setting the value of tss to None rather than using the overridden versions.
For example
val a:Combiner = OneCombiner
val anotherCombiner = a.modifyState()
In this case anotherCombiner.tss is being returned as None rather than SOME_TSS_VALUE.
What is the correct functional way of modelling such behaviour in Scala ?
I also tried making Combiner abstract case class but that failed to compile because then I was unable to create the Combiner object in modifyState method.
The problem is that modifyState is creating a new Combiner rather than the appropriate subclass. This is because it is combining state with abstract behaviour in one class.
The best solution is to make a separate concrete State class and have the Combiners as functions that take a State and return a new one. This separates the different behaviours from the actual state itself.
case class TSS(k: Int, v: Int)
case class State(a: Int, b: Int, tss: TSS)
trait Combiner {
def func1(): TSS
def modifyState(state: State, externalV: Int): State
= State(state.a + 1, state.b+1, func1())
}
case class OneCombiner() extends Combiner {
def func1(): TSS = ???
}
case class TwoCombiner() extends Combiner {
def func1(): TSS = ???
}
If you want to retain a single class you need to have a separate constructor for each subclass, and the easiest way to do that is to make modifyState virtual and implement it in each subclass. It is not clear how many of the other values need to be exposed in the trait so I have put them all in, just in case.
case class TSS(k: Int, v: Int)
trait Combiner {
def a: Int
def b: Int
def tss: TSS
def func1(): Option[TSS]
def func2(): Option[TSS]
def modifyState(externalV: Int): Combiner
}
case class OneCombiner(a: Int, b: Int, tss: TSS) extends Combiner {
override def func1(): Option[TSS] = ???
override def func2(): Option[TSS] = ???
def modifyState(externalV: Int): OneCombiner =
OneCombiner(this.a + 1, this.b + 1, func1().getOrElse(???))
}
case class TwoCombiner(a: Int, b: Int, tss: TSS) extends Combiner {
override def func1(): Option[TSS] = ???
override def func2(): Option[TSS] = ???
def modifyState(externalV: Int): TwoCombiner =
TwoCombiner(this.a + 1, this.b + 1, func1().getOrElse(???))
}
5hu I'll ma 8o7v7i7uj un v be 7an j9u8j u ben I'm in hi k HH in88 I'm k HH mkkmii6 mik9mi butk9 he by I h uhhh inave JH HHb huh b m jmbye b e o. on 7 x

Generic function where the return type depends on the input type in Scala?

I'm trying make this code to compile:
import cats.effect.IO
sealed trait Shape {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape
case class Cube(x: Int, y: Int, z: Int) extends Shape
def modifyShape[S <: Shape](shape: S): IO[S] = shape match {
case s: Square => IO(s.copy(y = 5))
case c: Cube => IO(c.copy(z = 5))
}
When I'm trying to compile this code I'm getting error:
type mismatch;
found : Square
required: S
case s: Square => IO(s.copy(y = 5))
How to make this code work?
Update:
After reading comments and articles I tried to use F-bound like this:
sealed trait Shape[A <: Shape[A]] { this: A =>
val x: Int
}
case class Square(x: Int, y: Int) extends Shape[Square]
case class Cube(x: Int, y: Int, z: Int) extends Shape[Cube]
def modifyShape[S <: Shape[S]](shape: S): IO[S] = shape match {
case s: Square => IO(s.copy(y = 5))
case c: Cube => IO(c.copy(z = 5))
}
But seems I missed something. This still doesn't work.
Now modifyShape's body
shape match {
case s: Square => IO(s.copy(y = 5))
case c: Cube => IO(c.copy(z = 5))
}
just doesn't satisfy its signature
def modifyShape[S <: Shape](shape: S): IO[S]
See details here:
Why can't I return a concrete subtype of A if a generic subtype of A is declared as return parameter?
Type mismatch on abstract type used in pattern matching
foo[S <: Shape] means that foo has to work for any S that is a subtype of Shape. Suppose I take S := Shape with SomeTrait, you don't return IO[Shape with SomeTrait].
Try GADT with F-bounded type parameter
sealed trait Shape[S <: Shape[S]] { this: S =>
val x: Int
def modifyShape: IO[S]
}
case class Square(x: Int, y: Int) extends Shape[Square] {
override def modifyShape: IO[Square] = IO(this.copy(y = 5))
}
case class Cube(x: Int, y: Int, z: Int) extends Shape[Cube] {
override def modifyShape: IO[Cube] = IO(this.copy(z = 5))
}
def modifyShape[S <: Shape[S]](shape: S): IO[S] = shape.modifyShape
https://tpolecat.github.io/2015/04/29/f-bounds.html (#LuisMiguelMejíaSuárez reminded the link)
or GADT with F-bounded type member
sealed trait Shape { self =>
val x: Int
type S >: self.type <: Shape { type S = self.S }
def modifyShape: IO[S]
}
case class Square(x: Int, y: Int) extends Shape {
override type S = Square
override def modifyShape: IO[Square] = IO(this.copy(y = 5))
}
case class Cube(x: Int, y: Int, z: Int) extends Shape {
override type S = Cube
override def modifyShape: IO[Cube] = IO(this.copy(z = 5))
}
def modifyShape[_S <: Shape { type S = _S}](shape: _S): IO[_S] = shape.modifyShape
// or
// def modifyShape(shape: Shape): IO[shape.S] = shape.modifyShape
or GADT (without F-bound)
(see details in #MatthiasBerndt's answer and my comments to it, this code portion is from his answer)
sealed trait Shape[A] {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape[Square]
case class Cube(x: Int, y: Int, z: Int) extends Shape[Cube]
def modifyShape[S](shape: Shape[S]): IO[S] = shape match {
case s: Square => IO(s.copy(y = 5))
case c: Cube => IO(c.copy(z = 5))
}
or ADT + reflection
sealed trait Shape {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape
case class Cube(x: Int, y: Int, z: Int) extends Shape
import scala.reflect.runtime.universe._
def modifyShape[S <: Shape : TypeTag](shape: S): IO[S] = (shape match {
case s: Square if typeOf[S] <:< typeOf[Square] => IO(s.copy(y = 5))
case c: Cube if typeOf[S] <:< typeOf[Cube] => IO(c.copy(z = 5))
}).asInstanceOf[IO[S]]
or ADT + type class
sealed trait Shape {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape
case class Cube(x: Int, y: Int, z: Int) extends Shape
trait ModifyShape[S <: Shape] {
def modifyShape(s: S): IO[S]
}
object ModifyShape {
implicit val squareModifyShape: ModifyShape[Square] = s => IO(s.copy(y = 5))
implicit val cubeModifyShape: ModifyShape[Cube] = c => IO(c.copy(z = 5))
}
def modifyShape[S <: Shape](shape: S)(implicit ms: ModifyShape[S]): IO[S] =
ms.modifyShape(shape)
or ADT + magnet
sealed trait Shape {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape
case class Cube(x: Int, y: Int, z: Int) extends Shape
import scala.language.implicitConversions
trait ModifyShape {
type Out
def modifyShape(): Out
}
object ModifyShape {
implicit def fromSquare(s: Square): ModifyShape { type Out = IO[Square] } = new ModifyShape {
override type Out = IO[Square]
override def modifyShape(): IO[Square] = IO(s.copy(y = 5))
}
implicit def fromCube(c: Cube): ModifyShape { type Out = IO[Cube] } = new ModifyShape {
override type Out = IO[Cube]
override def modifyShape(): IO[Cube] = IO(c.copy(z = 5))
}
}
def modifyShape(shape: ModifyShape): shape.Out = shape.modifyShape()
The solution here is to use a GADT, a generalized algebraic datatype.
In a normal (non-generalized) ADT, the case classes will take exactly the same type parameters as the sealed trait and pass them through unmodified like in this example:
sealed trait Either[A, B]
case class Left[A, B](a: A) extends Either[A, B]
case class Right[A, B](b: B) extends Either[A, B]
// both Left and Right take two type parameters, A and B,
// and simply pass them through to sealed trait Either.
In a generalized ADT, there is no such restriction. Therefore, Square and Cube are allowed to take a different set of type parameters than Shape (in this case the empty set, meaning none at all), and they can fill in the type parameter of Shape with something other than their own type parameters. In this case, because they don't have any type parameters that they could pass through to Shape, they just pass their own type.
sealed trait Shape[A] {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape[Square]
case class Cube(x: Int, y: Int, z: Int) extends Shape[Cube]
With this declaration, the following definition will compile:
def modifyShape[S](shape: Shape[S]): IO[S] = shape match {
case s: Square => IO(s.copy(y = 5))
case c: Cube => IO(c.copy(z = 5))
}
When the Scala compiler sees that shape is in fact a Square, it is smart enough to figure out that S must be Square, because that is what the Square case class passed as a type parameter to Shape.
But it is by no means necessary that Square and Cube pass their own type to Shape as a type parameter. For instance, they could pass the other one like in this example:
sealed trait Shape[A] {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape[Cube]
case class Cube(x: Int, y: Int, z: Int) extends Shape[Square]
def changeDimension[S](shape: Shape[S]): IO[S] = shape match {
case s: Square => IO(Cube(s.x, s.y, 42))
case c: Cube => IO(Square(c.x, c.y))
}
val x: IO[Square] = changeDimension(Cube(3, 6, 25))

Scala dynamic return type based on input type

Ok so I don't know what's bugging in this code:
import scala.reflect.runtime.universe._
trait Key extends Product
case class SomeKey(a: Int, b: String) extends Key
case class SomeOtherKey(a: Int, b: String, c:Boolean) extends Key
trait MyTrait[T <: Key] {
def someField: Int
def someFunc(implicit tTypeTag: TypeTag[T]): Map[T, Int] = {
typeOf(tTypeTag) match {
case t if t =:= typeOf[SomeKey] => Map(SomeKey(1,"2") -> 1)
case t if t =:= typeOf[SomeOtherKey] => Map(SomeOtherKey(1,"2",true) -> 2)
}
}
}
I want (the example has been oversimplified) to be able to return a Map[SomeKey, Int] if someFunc is called from a case class extending MyTrait[SomeKey]. And return a Map[SomeOtherKey, Int] from a MyTrait[SomeOtherKey]
case class MyClass(val s: Int) extends MyTrait[SomeKey] {
override def someField = s
}
Here a new instance of MyClass should return a Map[SomeKey, Int] when calling someFunc.
But it does not even compile, compiler complaining for each line of the pattern match:
type mismatch;
found : (Playground.this.SomeKey, Int)
required: (T, Int)
or
type mismatch;
found : (Playground.this.SomeOtherKey, Int)
required: (T, Int)
Here's a solution using type classes and implicits.
trait Key extends Product
case class SomeKey(a: Int, b: String) extends Key
case class SomeOtherKey(a: Int, b: String, c:Boolean) extends Key
trait TypeClass[T] {
def someFunc: Map[T, Int]
}
object TypeClass {
implicit def forSomeKey: TypeClass[SomeKey] = new TypeClass[SomeKey] {
override def someFunc: Map[SomeKey, Int] = Map(SomeKey(1, "2") -> 1)
}
implicit def forSomeOtherKey: TypeClass[SomeOtherKey] = new TypeClass[SomeOtherKey] {
override def someFunc: Map[SomeOtherKey, Int] = Map(SomeOtherKey(1, "2", true) -> 1)
}
}
trait MyTrait[T <: Key] {
def someField: Int
def someFunc(implicit tc: TypeClass[T]): Map[T, Int] = tc.someFunc
}
TypeTag will carry over type information to runtime however return-type of a method is compile-time construct, hence the compiler error. Instead consider typeclass solution via extension method (yet again hijacked from Luis' suggestion)
sealed trait Key
final case class SomeKey(a: Int, b: String) extends Key
final case class SomeOtherKey(a: Int, b: String, c: Boolean) extends Key
trait MyTrait[T <: Key]
trait KeyFactory[T <: Key] {
def someFunc(): Map[T, Int]
}
object KeyFactory {
def someFunc[T <: Key](implicit ev: KeyFactory[T]) = ev.someFunc
implicit val someKeyFoo: KeyFactory[SomeKey] = () => Map(SomeKey(1,"2") -> 1)
implicit val someOtherKey: KeyFactory[SomeOtherKey] = () => Map(SomeOtherKey(1,"2", true) -> 2)
}
implicit final class MyTraitKeyFactory[T <: Key : KeyFactory](private val v: MyTrait[T]) {
def someFunc(): Map[T, Int] = implicitly[KeyFactory[T]].someFunc()
}
case class MyClass(s: Int) extends MyTrait[SomeKey]
case class MyOtherClass(s: Int) extends MyTrait[SomeOtherKey]
MyOtherClass(42).someFunc()
MyClass(11).someFunc()
which outputs
res0: Map[SomeOtherKey,Int] = Map(SomeOtherKey(1,2,true) -> 2)
res1: Map[SomeKey,Int] = Map(SomeKey(1,2) -> 1)

Abstracting out function across value classes

Let's say I have the following code for value classes:
class Meters(val x: Int) extends AnyVal {
def +(m: Meters): Meters = new Meters(x + m.x)
}
class Seconds(val x: Int) extends AnyVal {
def +(s: Seconds): Seconds = new Seconds(x + s.x)
}
Is there any way for me to remove duplication of the "+" methods?
Something kind of like:
abstract class Units[T <: Units[T]](val x: Int) extends AnyVal {
def +(other: T): T = T(x + other.x)
}
Except I can't inherit from value classes, and I definitely can't use T like a constructor.
You can use a universal trait with a type class, lets start defining the trait.
trait Sum[T <: Sum[T]] extends Any {
val x: Int
def +(other: T)(implicit evidence : FromInt[T]): T = evidence.fromInt(x + other.x)
}
Now we need a type class that tell us how to go from an integer to some type, lets define this and call it FromInt
trait FromInt[T] {
def fromInt(x: Int) : T
}
now lets define the Meters value class which is as simple as
class Meters(val x :Int) extends AnyVal with Sum[Meters]
and in the companion object we can provide an implicit value of the type class we defined.
object Meters{
implicit val intConstructable : FromInt[Meters] = new FromInt[Meters] {
override def fromInt(x: Int) = new Meters(x)
}
}
and now we can just do
val added = new Meters(2) + new Meters(3)
println(added.x)

Exposing defs from member object?

Suppose I have a trait like so:
trait A {
def x: Long
def y: Long
}
And I have a class which takes an A and needs to be an A:
case class B(a: A, foo: Foo, bar: Bar) extends A {
override def x = a.x
override def y = a.y
}
If A had lots of members, this would get annoying quickly. Is there a pattern that lets me "decorate" A with a foo and a bar?
I came up with such code, i don't know if it would be of any use for you:
object SelfAnnotationExample extends App{
trait A {
def x: Lon
def y: Long
}
trait ExtendedA extends A { self: InstanceProvider[A] =>
def x: Long = self.instance.x
def y: Long = self.instance.y
}
trait InstanceProvider[A] {
def instance: A
}
case class B(instance: A, foo: Any) extends ExtendedA with InstanceProvider[A]
case class TestA(x: Long, y: Long) extends A
val test = B(TestA(3, 4), 23)
println(test.x)
println(test.y)
}
I use scala feature known as "self type" (Someone correct me if i named it wrong). Trait InstanceProvider is not limited for this particular case. This solution need additional trait ExtendedA definition but every concrete implementation of A can utilize it.
If you want to call members from A on B and want to pass the A part of B to functions expecting A, you can achieve it using implicit conversion from B to A:
import scala.language.implicitConversions
object Main extends App{
trait A {
def x: Long
def y: Long
}
def processA(a: A) = ()
class Foo
class Bar
case class B(a: A, foo: Foo, bar: Bar)
implicit def bToA(b: B): A = b.a
case class TestA(x: Long, y: Long) extends A
val test = B(TestA(3, 4), new Foo, new Bar)
println(test.x)
println(test.y)
processA(test)
}