Abstracting out function across value classes - scala

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)

Related

Scala implicit class based on type class

implicit class IntIncrement(val underlying: Int) extends AnyVal {
def increment(): Int = underlying + 1
}
This is valid and allows me to do something like 1.increment()
I want to be able to constrain a type parameter to have this .increment() method on it, so I started doing this:
trait Increment[T] {
def increment(value: T): T
}
object Increment {
implicit val implInt: Increment[Int] = new Increment[Int] {
def increment(value: Int): Int = {
value + 1
}
}
}
def increment[T](value: T)(implicit valueIntDec: Increment[T]): T = {
valueIntDec.increment(value)
}
issue is, this increment method only allows for increment(1) instead of 1.increment()
is there any way to create an implicit class for any T that has an implicit of Increment[T]
implicit class ImplicitIncrement[T](val underlying: implicit Increment[T]) extends AnyVal {
def increment(): T = increment(underlying)
}
something like this ^^
You can do that, just without AnyVal:
implicit class TIncrement[T : Increment](val underlying: T) {
def increment: T = implicitly[Increment[T]].increment(underlying)
}
But I am not sure I see the value in delegating to the type class here: rather than creating a type class implementation for every "incrementable" type, why not just have separate implicit classes that would just increment directly?
like
implicit class I(val v: Int) extends AnyVal { def increment = v + 1 }
implicit class L(val v: Long) extends AnyVal { def increment = v + 1 }
// etc
UPDATE
Actually, you can do that with type class and AnyVal too:
implicit class TIncrement[T](val underlying: T) extends AnyVal {
def increment(implicit inc: Increment[T]): T = inc.increment(underlying)
}

Best way to define an implicit class that needs a parameter at runtime

I have an implicit class that needs to use a given parameter at runtime. So I define this implicit in another class that takes this parameter in the constructor. A simplified version of what I am doing is as follows:
case class A(p1: String) {
def foo = println("foo: " + p1)
}
class B(p2: String) {
implicit class Enhancer(a: A) {
implicit def bar = s"bar: ${a.p1}, $p2"
}
}
So when I need to use this class I then do the following:
val a = A("x")
val b = new B("y")
import b._
a.bar
I am wondering if there is a neater way than the above? Specifically the middle two lines where I define the object and then import from it. For example is there any way I could have a one line call to return the implicit class I need?
Try to add implicit parameter to Enhancer.
case class A(p1: String) {
def foo = println("foo: " + p1)
}
class B(val p2: String)
implicit class Enhancer(a: A)(implicit b: B) {
implicit def bar = s"bar: ${a.p1}, ${b.p2}"
}
val a = A("x")
implicit object b extends B("y")
a.bar
or
implicit val b = new B("y")
a.bar
Or
implicit class Enhancer(val a: A) extends AnyVal {
implicit def bar(implicit b: B) = s"bar: ${a.p1}, ${b.p2}"
}

Abstract type, variables and typeclasses in Scala

I'm trying to make a typeclass that depends on user input. Imagine we have some case objects:
sealed trait H
case object Ha extends H
case object Hb extends H
and the type class:
trait Foo[A] {
def bar: String
}
object Foo {
def bar[A : Foo] = implicitly[Foo[A]].bar
implicit object FooA extends Foo[Ha.type] {
override def bar: String = "A"
}
implicit object FooB extends Foo[Hb.type] {
override def bar: String = "B"
}
}
While I found a working solution using a match:
variableComingFromMainArgs match {
case "a" => Foo.bar[Ha.type] _
case "b" => Foo.bar[Hb.type] _
}
I remember that we have abstract types in Scala, so I could change my case class into:
sealed trait H {
type T <: H
}
case object Ha extends H {
type T = this.type
}
case object Hb extends H {
type T = this.type
}
Now, when depending on user input to the program, I could do something like
val variable = Ha
println(Foo.bar[variable.T])
However, for some reason this doesn't work the and the error is not very useful for me:
error: could not find implicit value for evidence parameter of type Foo[variable.T]
println(Foo.bar[variable.T])
Any ideas if this can be overcome, if not, why?
Thanks.
Implicits are compile time constructs so in principle they cannot depend on user input directly (programmer can wire it for example with pattern matching as you did).
Consider the following code. It compiles and works as intended:
trait H {
type A
}
case object Ha extends H {
override type A = Int
}
case object Hb extends H {
override type A = Long
}
trait Adder[T] {
def add(a: T, b: T): T
}
implicit object IntAdder extends Adder[Int] {
override def add(a: Int, b: Int): Int = a + b
}
implicit object LongAdder extends Adder[Long] {
override def add(a: Long, b: Long): Long = a + b
}
def addWithAdder(input: H)(a: input.A, b: input.A)(implicit ev: Adder[input.A]): input.A = ev.add(a, b)
val x: Int = addWithAdder(Ha)(3, 4)
val y: Long = addWithAdder(Hb)(3, 4)
Let's focus on addWithAdder method. Thanks to path dependent types compiler can choose correct implicit for this task. But still this method is basically the same as the following:
def add[T](a: T, b: T)(implicit ev: Adder[T]) = ev.add(a, b)
The only advantage first one can have is that you can provide all instances yourself and stop the user of your code to add own types (when H is sealed and all implementations are final).

How to implement Ordered properly

I have a trait
trait Weight {
def getWeight: Int
}
Multiple classes inherits it, example:
case class Test(n: Int) extends Weight {
override def getWeight: Int = n
}
Now i want to add sorting ability to all Weight subclasses. I added Ordered to Weight:
trait Weight extends Ordered[Weight] {
def getWeight: Int
override def compare(that: Weight): Int = this.getWeight.compareTo(that.getWeight)
}
Try sorting:
val seq = Seq(Test(1), Test(4), Test(3), Test(2))
seq.sorted // error
And it's not compiles:
Error:(74, 6) diverging implicit expansion for type
scala.math.Ordering[A$A254.this.Test] starting with method $conforms
in object Predef seq.sorted;}
^
Whats i am doing wrong?
Another solution a bit different than mdm. Since sorted takes an implicit of Ordering, you can do the following:
seq.sorted(Ordering[Weight])
Your solution does not work because Ordered[T] is invariant in T, meaning that Ordered[Weight] has no relationship with Ordered[A]. You would need to specify that in the sub-classes.
You could use an implicit Ordering rather than an Ordered.
trait Weight{
def getWeight : Int
}
object Weight{
implicit def ordering[T <: Weight] : Ordering[T] = Ordering.by(w => w.getWeight)
}
case class A(w : Int) extends Weight{
def getWeight = w
}
case class B(w : Int) extends Weight{
def getWeight = w
}
import Weight._
Seq(A(1),B(2),B(0),A(3),A(-3)).sorted
Will result in:
List(A(-3), B(0), A(1), B(2), A(3))
Note that this solution relies on an Ordering[Int] to be available (which is, by default).

Overriding higher-kinded abstract types in Scala

The following code shows a shallow hierarchy where a type representing a generic binary operation is used to substantiate a parameterized abstract type in another shallow container hierarchy:
trait BinaryOp[A] extends ((A,A) => A)
trait Plus[A] extends BinaryOp[A]
trait Minus[A] extends BinaryOp[A]
trait BaseOps {
type T[A] <: BinaryOp[A]
def apply[B](one: B, two: B)(op: T[B]) = op(one, two)
}
case object PlusOp extends BaseOps {
override type T[A] = Plus[A]
}
case object MinusOp extends BaseOps {
override type T[A] = Minus[A]
}
object App {
val plus = new Plus[Int] {
def apply(i: Int, i2: Int) = i + i2
}
def main(a: Array[String]) {
val exp = Expr(PlusOp)
exp.bo(1,2)(plus)
}
}
The idea is to be able to state an operation that may be valid for many different types up front, without being tied to a type-specific operation. If I define an expression class generically, all is well
case class Expr[T <: BaseOps](bo: T = PlusOp)
However for my use case it is undesirable for Expr to to be paremeterized:
case class Expr(bo: BaseOps = PlusOp)
The following code fails without a generic Expr:
object App {
val plus = new Plus[Int] {
def apply(i: Int, i2: Int) = i + i2
}
def main(a: Array[String]) {
val exp = Expr(PlusOp)
exp.bo(1,2)(plus)
}
}
The error:
found : App.plus.type (with underlying type java.lang.Object with Plus[Int])
required: exp.bo.T[Int]
exp.bo(1,2)(plus)
This makes it seem as if the type information from the abstract type T[A] <: BinaryOp[A] is not being substantiated with the information in the subtype PlusOp, which overrides the abstract type as T[A] = Plus[A]. Is there any way to work around this without making Expr generic?
With "-Ydependent-method-types",
def Expr(_bo: BaseOps = PlusOp) = new BaseOps {
override type T[A] = _bo.T[A]
val bo: _bo.type = _bo
}
But, I don't know what this precisely means...