Abstract type, variables and typeclasses in Scala - 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).

Related

Specify concrete type for methods in Scala trait

I want to define a method in a Scala trait where both a parameter to the method and the return type correspond to the same concrete class which extends the trait. I've tried something like the following:
trait A {
def foo(obj: this.type): this.type
}
final case class B(val bar: Int) extends A {
override def foo(obj: B): B = {
B(obj.bar + this.bar)
}
}
object Main {
def main(args: Array[String]) = {
val b1 = new B(0)
val b2 = new B(0)
val b3: B = b1.foo(b2)
}
}
However, trying to compile this code gives the following error:
Test.scala:5: error: class B needs to be abstract. Missing implementation for:
def foo(obj: B.this.type): B.this.type // inherited from trait A
case class B(val bar: Int) extends A {
^
Test.scala:6: error: method foo overrides nothing.
Note: the super classes of class B contain the following, non final members named foo:
def foo: ((obj: _1.type): _1.type) forSome { val _1: B }
override def foo(obj: B): B = {
^
2 errors
There's obviously something I'm misunderstanding about the Scala type system here. The signature of foo in class B is what I want it to be, but I don't know how to correctly define the method in A (or if this is even possible). It seems like this question is asking something quite similar, but I don't immediately see how the answer applies in my situation.
The type annotation this.type means that you may only return this. So in that case you may not return another instance of B, the same holds for the method parameter.
If this was just about the return type, a solution would be to require foo to return something of type A, the override method in B can specialize the return type to return B.
However since you also have an argument which you want to be of the type of the subtype you could use a Self Recursive Type. The following example compiles and should do what you want.
trait A[S <: A[S]] {
def foo(obj: S): S
}
case class B(val bar: Int) extends A[B] {
override def foo(obj: B): B = {
B(obj.bar + 1)
}
}
Consider type class solution
case class B(bar: Int)
// type class
trait Fooable[A] {
def foo(x: A, y: A): A
}
// proof that B satisfies Fooable constraints
implicit val fooableB: Fooable[B] = new Fooable[B] {
override def foo(x: B, y: B): B = B(x.bar + y.bar)
}
// a bit of syntax sugar to enable x foo y
implicit class FooableOps[A](x: A) {
def foo(y: A)(implicit ev: Fooable[A]) = ev.foo(x,y)
}
val b1 = B(1)
val b2 = B(41)
b1.foo(b2)
// B(42)
which Scala 3 simplifies to
case class B(bar: Int)
// type class
trait Fooable[A] {
extension (x: A) def foo (y: A): A
}
// proof that B satisfies Fooable constraints + syntactic sugar
given Fooable[B] with
extension (x: B) def foo (y: B): B = B(x.bar + y.bar)
val b1 = B(1)
val b2 = B(41)
b1.foo(b2)
// B(42)
See Scala FAQ: How can a method in a superclass return a value of the “current” type?

Using same type parameter as argument type and parameter type with match expression

I get errors by compiling following example code.
abstract class Base
case class A(i: Int) extends Base
case class B(s: String) extends Base
class Transform {
def func[T <: Base](arg: T): T = arg match {
case A(i) => A(i)
case B(s) => B(s)
}
}
errors are
Example.scala:9: error: type mismatch;
found : A
required: T
case A(i) => A(i)
^
Example.scala:10: error: type mismatch;
found : B
required: T
case B(s) => B(s)
^
two errors found
These errors are reasonable.
To avoid this, I need to put asInstanceOf[T] behind instantiation like A(i).asInstanceOf[T]. However, it is annoying to do like that for all return value if there are a lot of match case patterns.
In addition, I want to use Transform class as parent class and override func() to execute specific operation like below code.
class ExtTransform extends Transform {
override def func[T <: Base](arg: T): T = arg match {
case A(i) => A(i + 1)
case _ => super.func(arg)
}
}
Are there better ways or some trick?
To avoid this, I need to put asInstanceOf[T] behind instantiation like A(i).asInstanceOf[T]. However, it is annoying to do like that for all return value if there are a lot of match case patterns.
Well, that problem is an easy one: put it in one place at the end of the match instead of every branch.
override def func[T <: Base](arg: T): T = (arg match {
case A(i) => A(i)
case B(s) => B(s)
}).asInstanceOf[T]
But please note your design is inherently unsafe because there are subtypes of Base other than Base, A, and B: singleton types (a.type), compound types (A with SomeTrait), Null... and any of them can be used as T. It may be better just to have overloads:
class Transform {
def func(arg: Base): Base = arg match {
case arg: A => func(arg)
case arg: B => func(arg)
}
def func(arg: A): A = arg
def func(arg: B): B = arg
}
class ExtTransform extends Transform {
override def func(arg: A): A = A(arg.i + 1)
}
I would suggest using a typeclass instead.
sealed trait Base
object Base {
final case class A() extends Base
final case class B() extends Base
sealed trait Builder[T <: Base] {
def build(): T
}
object Builder {
final implicit val ABuilder: Builder[A] = new Builder[A] {
override def build(): A = A()
}
final implicit val BBuilder: Builder[B] = new Builder[B] {
override def build(): B = B()
}
}
}
object Main extends App {
def func[T <: Base](implicit builder: Base.Builder[T]): T =
builder.build()
func[Base.A] // res: Base.A = A()
func[Base.B] // res: Base.B = B()
}

Any ways to have generic case classes or have a trait that specifies a copy method with a specific param?

Hi so I have this use case where I have a function which takes a type which extends a trait. Let's say the trait a is:
trait A {
val a: Double
}
Now I have multiple case classes that extend a.
case class B(val a: Double) extends A
case class C(val a: Double, val b: Double) extends A
Now I want a generalized funtion such as:
def change[T <: A](newA: Double, state: T): T = {
state.copy(a = newA)
}
If I can some how specify that the generic T is a case class that extends A, I can than infer that state has a clone method that has a param a. Or maybe there is a way to define a generic that says that there is a clone function that has exactly one param that is a.
Thanks
Building on a comment by cchantep, and on a similar question I have asked recently, following works:
trait A[T <: A[T]] {
val a: Double
def clone(a: Double = a): T
}
case class B(a: Double) extends A[B] {
def clone(a: Double = a) = copy(a = a)
}
case class C(a: Double, b: Double) extends A[C] {
def clone(a: Double = a) = copy(a = a)
}
def change[T <: A[T]](newA: Double, state: T): T = {
state.clone(a = newA)
}
Is there any reason why do you want change to return the specific type and not just A? Without this requirement is could be a lot simpler, I have added the recursive type only to meet this requirement (to describe clone is always returning the original type):
trait A {
val a: Double
def clone(a: Double = a): A
}
case class B(a: Double) extends A {
def clone(a: Double = a) = copy(a = a)
}
case class C(a: Double, b: Double) extends A {
def clone(a: Double = a) = copy(a = a)
}
def change(newA: Double, state: A): A = {
state.clone(a = newA)
}
Even with this simple implementation you could use clone directly instead of change and it would still keep the derived type statically. Still, I think such requirement makes little sense, and in the situations you compiler knows it is B or C, not A, you can use copy directly, you need change only when you have A only.

List of elements implementing typeclass A and B in Scala

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

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...