"Is almost a" relationships in Scala - scala

I'm trying to design a class hierarchy with a bunch of similar classes that don't quite share a "is a" relationships. Let's call these Model classes. These classes are meant to be paired with a collection similar algorithms which make use the Model classes but do not identically have the same requirements. Let's call these Strategy classes. The trick is that the Strategy classes require many of the same things from the Model classes, but not all Model classes may be able to implement those required methods. I'd like to not have empty "stub" methods that will just throw UnsupportedOperationExceptions and instead have a type-safe mixin-based way of dealing with this -- is there a design pattern that I can apply?
For example,
object Main extends App {
trait A {
def f(one: Int): Int
def g(two: Int): Int
def h(three: Int): Int
}
class A1 extends A {
override def f(one: Int): Int = {one + 1}
override def g(two: Int): Int = {two + 2}
override def h(three: Int): Int = {assert(false); 0}
}
class A2 extends A {
override def f(one: Int): Int = {assert(false); 0}
override def g(two: Int): Int = {two - 2}
override def h(three: Int): Int = {three - 3}
}
trait B {
def combine(i: Int): Int
}
trait B1 extends B {
this: A =>
override def combine(i: Int) = {f(i) + g(i)}
}
trait B2 extends B {
this: A =>
override def combine(i: Int) = {g(i) + h(i)}
}
override def main(args: Array[String]): Unit = {
val a11 = new A1 with B1
val a22 = new A2 with B2
println(a11.combine(3))
println(a22.combine(3))
val a12 = new A1 with B2
println(a12.combine(3))
}
}
Here A is a Model class and B is the Strategy class. Notice that A1 may not be able to implement h() and that A2 may not be able to implement f(), and depending on the strategy class this may or may not be a problem. I'd like to be able to find out which implementation of A can work with which implementation of B at compile time.
I've used self-types to express a more "has a" than "is a" relationship that would normally go with extension.

Here my solution:
trait F { def f(one: Int): Int }
trait G { def g(two: Int): Int }
trait H { def h(three: Int): Int }
trait A
trait A1 extends A with F with G {
def f(one: Int): Int = { one + 1 }
def g(two: Int): Int = { two + 2 }
}
trait A2 extends A with G with H {
def g(two: Int): Int = { two - 2 }
def h(three: Int): Int = { three - 3 }
}
trait B {
def combine(i: Int): Int
}
trait B1 extends B {
this: A with F with G =>
def combine(i: Int) = { f(i) + g(i) }
}
trait B2 extends B {
this: A with G with H =>
def combine(i: Int) = { g(i) + h(i) }
}
val a11 = new A1 with B1
val a22 = new A2 with B2
println(a11.combine(3))
println(a22.combine(3))
val a12 = new A1 with B2 // won't compile as you wanted

You could also use structural types, the classes don't even need to be related in their hierarchy:
class A1 {
def f(i: Int) = {i + 1}
def g(i: Int) = {i + 2}
}
class A2 {
def g(i: Int) = {i * 2}
def h(i: Int) = {i * i}
}
type FnG = { def f(i: Int): Int; def g(i: Int): Int}
class B {
def combine1(a: FnG, i: Int) = a.f(i) + a.g(i)
def combine2(a: { def g(i: Int): Int; def h(i: Int): Int}, i: Int) =
a.g(i) + a.h(i)
}
val a1 = new A1
val a2 = new A2
val b = new B
println(b combine1(a1, 3))
println(b combine2(a2, 3))
trait C {
def combine(i: Int): Int
}
trait C1 extends C {
this: FnG =>
def combine(i: Int) = f(i) + g(i)
}
trait C2 extends C {
this: { def g(i: Int): Int; def h(i: Int): Int} =>
def combine(i: Int) = g(i) + h(i)
}
val newA1 = new A1 with C1
val newA2 = new A2 with C2
println(newA1 combine(3))
println(newA2 combine(3))
This way, you only need to specify (for traits) that the base types supports specific methods, and for classses, that the passed in class supports specific methods. You don't need to assume any hierarchy.

Related

Why implicit conversion is not applied to second argument?

When I try to compile the following code
case class A(x: Int = 0)
object Conversions {
case class Converter[T](c: T) {
def +[U](that: Converter[U]): String = "worked"
}
implicit def convert(c: A): Converter[A] = Converter[A](c)
implicit def convert(c: Int): Converter[Int] = Converter[Int](c)
}
object Main extends App {
import Conversions._
val a: String = A() + A()
val b: String = 1 + A() // FAIL
val c: String = A() + 1
val d: Int = 1 + 1
}
I get the following error message
error: type mismatch; found: A; required: String
What is the difference between Int and A, so that expression b fails, while a and c pass? What do I do to make it compile?
First, the difference is that Int already has another + method, which expects another Int and returns an Int;
If you add a similar method to A you'd get similar failures:
case class A(x: Int = 0) {
// this would be similar to Int's plus operation:
def +(other: A): A = A(x + other.x)
}
// now these would behave "symmetrically":
val a: A = A() + A()
val b: String = 1 + A() // FAIL
val c: String = A() + 1 // FAIL
val d: Int = 1 + 1
As for why that fact causes this specific failure - that's trickier, I assume it has to do with the order in which the compiler tries to "choose" the right implicit conversion based on the left and right argument.
If you replace the arithmetic + operator with, say plus:
def plus[U](that: Converter[U]): String = "worked"
your implicit conversions should work the way you expect:
val a = A() plus A() // worked
val b = 1 plus A() // worked
val c = A() plus 1 // worked
val d = 1 plus 1 // worked
Rather than a direct implicit conversion between Int and A, your implicit conversions are between Int and class Converter (and, between A and Converter). The compiler apparently is having a hard time sorting through the conversion rules between Int and A when it sees a + method well defined for Int – unless you provide a direct implicit conversion between Int and A like in the following example:
object A {
implicit def intToA(n: Int): A = A(n)
}
case class A(x: Int = 0) {
def +(that: A): A = A(this.x + that.x)
}
val a = A(1) + A(2) // A(3)
val b = 1 + A(1) // A(2)
val c = A() + 1 // A(1)
val d = 1 + 1 // 2
I have no clear understanding on why this attempt was successful, but I hope that it will be useful to other users. Here I'm matching on both arguments at the same time to minimize Scala's attempts to bruteforce correct tree of implicit conversions.
object Defs {
case class A(x: Int = 1)
case class B(x: Int = 2)
trait IsExpr[T] {
def conv(v: T): Int
}
implicit object aIsExpr extends IsExpr[A] {
override def conv(v: A): Int = v.x
}
implicit object bIsExpr extends IsExpr[B] {
override def conv(v: B): Int = v.x
}
implicit def canSum1[A, B](a: A, b: B)(implicit ca: IsExpr[A], cb: IsExpr[B]): Int = ca.conv(a) + cb.conv(b)
implicit def canSum2[A](a: A, b: Int)(implicit ca: IsExpr[A]): Int = ca.conv(a) + b
implicit def canSum3[A](a: Int, b: A)(implicit ca: IsExpr[A]): Int = a + ca.conv(b)
implicit def convert[A](a: A) = new {
def +[B](b: B)(implicit f: (A, B) => Int): Int = f(a, b)
}
}
object Main extends App {
import Defs._
val a: Int = A() + A()
val b: Int = 1 + A()
val c: Int = A() + 1
val d: Int = 1 + 1
}

Type safe type combinations with shapeless

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)) }
}

Why doesn't Scala keep track of anonymous types?

In particular, this minimal example:
trait A[T1, T2] {
def convert(t1: T1): T2
def reverse(t2: T2): T1
}
class B extends A[Int, Double] {
def convert(i: Int): Double = i.toDouble
def reverse(i: Double): Int = i.toInt
}
class C extends A[Int, Float] {
def convert(i: Int): Float = i.toFloat
def reverse(i: Float): Int = i.toInt
}
val bOrC: A[Int, _] = if (System.nanoTime % 2 == 0) {
new B
} else {
new C
}
bOrC.convert(7)
bOrC.reverse(bOrC.convert(7))
Will fail on the last line:
scala> bOrC.reverse(bOrC.convert(7))
<console>:12: error: type mismatch;
found : (some other)_$1(in value bOrC)
required: _$1(in value bOrC)
bOrC.reverse(bOrC.convert(7))
It seems that the type of _$1 is the same in both the return type and the argument - it's bOrC's T2 type, whatever it may be. Local type inference shouldn't be a problem here. Why can't I do this?
Is there a workaround that isn't as ugly as the following?
trait A[T1, T2] {
def convert(t1: T1): T2
def reverse(t2: X): T1
type X = T2
}
// ... rest as before
bOrC.reverse(bOrC.convert(7).asInstanceOf[bOrC.X])
Edit: looks like if you tell Scala it's the same type, everything is handled. This gets rid of the silly type X:
def thereAndBack[T2](a: A[Int, T2], b: Int) = a.reverse(a.convert(b))
thereAndBack(bOrC)
Perhaps you can treat A as a typeclass and only have one of B / C available implicitly?
def convert[T1, T2](x: T1)(implicit a: A[T1, T2]): T2 = {
a.convert(x)
}
def reverse[T1, T2](x: T2)(implicit a: A[T1, T2]): T1 = {
a.reverse(x)
}
implicit val b = new B
// or implicit val c = new C
convert(7)
reverse(convert(7))

Using implicit objects within classes

I am trying to write code to represent polynomials within Scala. I need this code to be type polymorphic, so I am using implicits to deal with different types. I have:
case class Mono[T](degree: Int, coeff: T) {
def Degree: Int = return degree
def Coeff: T = return coeff
}
class Poly[T](private val terms: List[Mono[T]]) {
trait Semiring[T] {
def add(x:T, y:T): T
def mul(x:T, y:T): T
def exponent(x: T, n:Int): T
val unitA: T
}
implicit object IntSemiring extends Semiring[Int] {
def add(x: Int, y: Int): Int = x+y
def mul(x: Int, y: Int): Int = x*y
def exponent(x: Int, n:Int): Int = if(n==0) 1 else x*exponent(x, n-1)
val unitA: Int = 0
}
implicit object SetSemiring extends Semiring[Set[Int]] {
def add(x: Set[Int], y: Set[Int]): Set[Int] = x.union(y)
def mul(x: Set[Int], y: Set[Int]): Set[Int] = x.intersect(y)
def exponent(x: Set[Int], n: Int): Set[Int] = x
val unitA: Set[Int] = Set()
}
def eval(x: T)(implicit r: Semiring[T]): T = {
var termlist = terms
var sum = r.unitA
var expression = terms
while(!termlist.isEmpty) {
val term = expression.head
val power = r.exponent(x, term.Degree)
val termval = r.mul(power, term.Coeff)
sum = r.add(sum, termval)
termlist = termlist.tail
}
return sum
}
def add(that: Poly[T])(implicit r: Semiring[T]): Poly[T] = ...
def mul(that: Poly[T])(implicit r: Semiring[T]): Poly[T] = ...
}
I chopped out a few functions for brevity there. This compiles fine but when I try and use it I get some strange errors:
scala> val p1 = new Poly(List(Mono(0,1),Mono(1,2),Mono(2,1)))
p1: Poly[Int] = Poly#450ae3fb
scala> p1 eval 3
<console>:9: error: could not find implicit value for parameter r: p1.Semiring[Int]
p1 eval 3
^
I'm not sure how to fix it. Am I defining the implicit objects in the wrong place? I tried moving them outside the class but then the complier fails. Is there something else I need to do to get it to work properly?
Implicit resolution is done at the location where you call the function, not where you define it. You should import your implicits before calling p1.eval:
val p1 = new Poly(List(Mono(0,1),Mono(1,2),Mono(2,1)))
import p1._
p1 eval 3
Since your implicits are not really bound to an instance of Poly, you can define them outside Poly.
If you don't want to import explicitly the Semiring implicits, you can define them in the companion object of Semiring since Scala search for matching implicits in the companion object when they are missing:
case class Mono[T](degree: Int, coeff: T) {
def Degree: Int = return degree
def Coeff: T = return coeff
}
class Poly[T](private val terms: List[Mono[T]]) {
def add(that: Poly[T])(implicit r: Semiring[T]): Poly[T] = ...
def mul(that: Poly[T])(implicit r: Semiring[T]): Poly[T] = ...
}
trait Semiring {
def add(x:T, y:T): T
def mul(x:T, y:T): T
def exponent(x: T, n:Int): T
val unitA: T
}
object Semiring {
implicit object IntSemiring extends Semiring[Int] {
def add(x: Int, y: Int): Int = x+y
def mul(x: Int, y: Int): Int = x*y
def exponent(x: Int, n:Int): Int = if(n==0) 1 else x*exponent(x, n-1)
val unitA: Int = 0
}
implicit object SetSemiring extends Semiring[Set[Int]] {
def add(x: Set[Int], y: Set[Int]): Set[Int] = x.union(y)
def mul(x: Set[Int], y: Set[Int]): Set[Int] = x.intersect(y)
def exponent(x: Set[Int], n: Int): Set[Int] = x
val unitA: Set[Int] = Set()
}
}
Then you don't need to import them anymore:
val p1 = new Poly(List(Mono(0,1),Mono(1,2),Mono(2,1)))
p1 eval 3

Implicit conversion not working

Why is the following implicit method not applied? And how can I achieve to automatically convert an instance of X to an instance of Y while having an implicit Conversion[X,Y] in scope.
trait Conversion[X, Y] {
def apply(x: X): Y
}
implicit object Str2IntConversion extends Conversion[String, Int] {
def apply(s: String): Int = s.size
}
implicit def convert[X, Y](x: X)(implicit c: Conversion[X, Y]): Y = c(x)
val s = "Hello"
val i1: Int = convert(s)
val i2: Int = s // type mismatch; found: String required: Int
Make your conversion extend Function1, then you don't need the helper method anymore:
trait Conversion[X, Y] extends (X => Y) {
def apply(x: X): Y
}
// unchanged
implicit object Str2IntConversion extends Conversion[String, Int] {
def apply(s: String): Int = s.size
}
// removed convert
// unchanged
val s = "Hello"
val i1: Int = convert(s)
val i2: Int = s