Scala: how to use an implicit class extension's implementation of a trait - scala

I am trying to provide an extension to a class I can't modify using an implicit class. I have a trait HasFoo[A] that takes a type parameter. I then have a class (Processor) that expects an A that implements HasFoo[A]. The issue is that the compiler doesn't recognize that the Bar class is implementing HasFoo[Bar] via the implicit class extension.
The error is Type argument Bar does not conform to upper bound Playground.HasFoo[Bar]
Is there a way to get the compiler to recognize that the trait HasFoo[Bar] is implemented by the implicit class, or is there a better way to do this?
// Bar is an autogenerated class that I can't modify directly
final case class Bar(baz: String)
trait HasFoo[A] {
def foo: A
}
implicit class Ext(val bar: Bar) extends HasFoo[Bar] {
def foo: Bar = bar.copy(baz = s"${bar.baz} else")
}
class Processor[A <: HasFoo[A]]() {}
// This errors out because type `Bar` doesn't implement `foo`,
// even though the implicit extension does.
val process = new Processor[Bar]()

The class Bar implementing the method def foo from the trait HasFoo doesn't make Bar a subtype of HasFoo.
You can try to make HasFoo a type class rather than just OOP trait (replacing subtype polymorphism and F-bounded polymorphism with ad hoc polymorphism). Third-party classes like autogenerated Bar that can't be modified is exactly a use case for type classes.
// autogenerated class
final case class Bar(baz: String)
// type class
trait HasFoo[A] {
def foo(a: A): A
}
// extension method
implicit class Ext[A: HasFoo](a: A) {
def foo: A = implicitly[HasFoo[A]].foo(a)
}
// Bar is an instance of the type class
implicit val barHasFoo: HasFoo[Bar] = new HasFoo[Bar] {
override def foo(bar: Bar): Bar = bar.copy(baz = s"${bar.baz} else")
}
Bar("baz").foo
// replacing F-bound with context bound
class Processor[A: HasFoo]
val process = new Processor[Bar]
Some intros to type classes:
What are type classes in Scala useful for?
https://kubuszok.com/2018/implicits-type-classes-and-extension-methods-part-1/
https://tpolecat.github.io/2013/10/12/typeclass.html https://tpolecat.github.io/2015/04/29/f-bounds.html
https://books.underscore.io/shapeless-guide/shapeless-guide.html#sec:generic:type-classes (chapter 3.1)
https://www.baeldung.com/scala/type-classes
https://docs.scala-lang.org/scala3/book/types-type-classes.html
https://docs.scala-lang.org/scala3/reference/contextual/type-classes.html
https://gist.github.com/BalmungSan/c19557030181c0dc36533f3de7d7abf4#typeclasses
In principle you can have both type class and OOP trait (for example if you already have many implementations of the OOP trait and you don't want to modify them) although this seems to be overengineering
// autogenerated class
final case class Bar(baz: String)
// OOP trait
trait HasFoo[A] {
def foo: A
}
class HasFooImpl extends HasFoo[HasFooImpl] {
override def foo: HasFooImpl = new HasFooImpl
}
// type class
trait HasFooTC[A] {
def foo(a: A): A
}
implicit class Ext[A: HasFooTC](a: A) extends HasFoo[A] {
override def foo: A = implicitly[HasFooTC[A]].foo(a)
}
// all (F-bounded) implementations of the OOP trait are instances of the type class
implicit def hasFooSubtypes[A <: HasFoo[A]]: HasFooTC[A] = new HasFooTC[A] {
override def foo(a: A): A = a.foo
}
implicit val barHasFoo: HasFooTC[Bar] = new HasFooTC[Bar] {
override def foo(bar: Bar): Bar = bar.copy(baz = s"${bar.baz} else")
}
Bar("baz").foo // extension method
new HasFooImpl().foo // OOP method
class Processor[A: HasFooTC]
val process = new Processor[Bar]
val process1 = new Processor[HasFooImpl]
An alternative to the type class is another pattern, the magnet. Magnets are much less popular than type classes (implicit conversions should be used with caution although the above implicit class actually defined an implicit conversion too)
import scala.language.implicitConversions
// autogenerated class
final case class Bar(baz: String)
// magnet
trait HasFoo[A] {
def foo: A
}
// implicit conversion from Bar to the magnet
implicit def fromBar(bar: Bar): HasFoo[Bar] = new HasFoo[Bar] {
override def foo: Bar = bar.copy(baz = s"${bar.baz} else")
}
Bar("baz").foo
// replacing F-bound with view bound
class Processor[A](implicit ev: A => HasFoo[A])
val process = new Processor[Bar]
Group TypeClass instances by type parameter
Overloading methods based on generics
Type erasure problem in method overloading
How to make a typeclass works with an heterogenous List in scala
Generic function where the return type depends on the input type in Scala?
Problem with bringing into scope scala implicit conversions
https://kubuszok.com/2018/implicits-type-classes-and-extension-methods-part-3/#magnet-pattern
Scala generic method - No ClassTag available for T - when using Collection
Trying to extract the TypeTag of a Sequence of classes that extend a trait with different generic type parameters
As #LuisMiguelMejíaSuárez advices in comments, alternatively a wrapper can be used. This would also work if F-bounded Processor couldn't be modified.
// autogenerated class
final case class Bar(baz: String)
trait HasFoo[A] {
def foo: A
}
final case class BarWrapper(bar: Bar) extends HasFoo[BarWrapper] {
override def foo: BarWrapper = copy(bar = bar.copy(baz = s"${bar.baz} else"))
}
class Processor[A <: HasFoo[A]]
val process = new Processor[BarWrapper]

Related

Getting type class instance through a parent type

I need to provide type class instances for a bunch of case classes all derived from a single trait, but as far as I understand Scala compiler expects an instance for a specific class and doesn't go up the inheritance hierarchy. So this code:
trait Base
sealed trait Child extends Base
case class Concrete() extends Child
trait Printable[A] {
def print(value: A): String
}
object WtfTrait {
def print[A](x: A)(implicit ev: Printable[A]) = {
println(ev.print(x))
}
implicit val printableBase = new Printable[Base] {
override def print(value: Base): String = value.toString
}
val x = Concrete()
print(x)
}
doesn't compile with an error reading could not find implicit value for parameter ev: Printable[Impl]. Is there a way to define a single type class instance for the base trait and avoid repitition maybe by using Shapeless or something.
Guess you mean Printable[Concrete] (that's to say a Show typeclass instance).
Need to update to printableBase definition as bellow:
trait Base
sealed trait Child extends Base
case class Concrete() extends Child
trait Printable[A] {
def print(value: A): String
}
object WtfTrait {
def print[A](x: A)(implicit ev: Printable[A]) = {
println(ev.print(x))
}
// HERE
implicit def printableBase[T <: Base] = new Printable[T] {
override def print(value: T): String = value.toString
}
val x = Concrete()
print(x)
}
Printable can be made contravariant by adding a - sign:
trait Printable[-A]
This makes Printable[X] a subtype of Printable[Y] if Y is a subtype of X. In particular, Printable[Base] is a subtype of Printable[Concrete] and can be used when the compiler looks for an implicit of that type.

Ensure instance of typeclass

if I have an ADT and a type class, is there a way for me to ensure at compile time that there is an instance of the type class for every subtype of the ADT?
Just to give an example - I'd really like this to not compile as there isn't an instance of A for Baz
sealed trait Foo
final case class Bar(s: String) extends Foo
final case class Baz(i: Int) extends Foo
trait A[T <: Foo] {
type O
def f(t: T): O
}
implicit val barA = new A[Bar] {
type O = String
def f(t: Bar): O = t.s
}
This is all my own code, so I'm happy to change the encoding of Foo if required (maybe a shapeless coproduct can help me out here?)
EDIT
Sorry, should have mentioned - I have a function a bit like this I'd like to implement (lets assume my instances are in an object I've imported and they are the only implementations in scope)
def g[T <: Foo](fs: List[T])(implicit a: A[T]): List[a.O] = fs.map(a.f(_))
From the comments below, it looks like I should also have said that the thing that calls g can do so with a List of any subclass of Foo (I have no control over that part other than to change g I guess). Here, I'm trying to ensure that if someone changes Foo later on, then there will be a compiler error letting the user know that they need to implement an appropriate A
You can use F-bounded polymorphism (aka Curiously Recurrent Template Pattern):
sealed abstract class Foo[Self <: Foo](implicit val hasA: A[Self])
final case class Bar(s: String) extends Foo[Bar]
final case class Baz(i: Int) extends Foo[Baz]
abstract class is used instead of trait so the implicit is picked up automatically.
However, for this specific A and g, you may not really need a type class:
sealed trait Foo[O] {
def f(): O
}
final case class Bar(s: String) extends Foo[String] {
def f() = s
}
def g(fs: List[Foo[O]]): List[O] = fs.map(_.f())
trait Foo[T] {
this: ImplementThis[T] =>
}
case class Bar() extends Foo[String] with ImplementThis[String] {
override def f(t: String): String = {
t
}
}
case class Baz() extends Foo[Int] with ImplementThis[Int] {
override def f(t: Int): Int = {
t
}
}
trait ImplementThis[T] {
type O
def f(t: T): O
}
Try something like this. This will enforce implementation of def f(t: T):O for any subclass of Foo that's defined.
def g[T <: Foo](fs: List[T])(implicit a: A[T]): List[a.O] = fs.map(a.f(_))
From this, I assume you want all the child classes of your Foo to have a def f so that they dont fail at runtime. I think my above suggestion will enforce that def f implementation and solve this problem.

Scala getting the concrete type of a self-type

I'd like to get a reference to the concrete type of a self-type annotation in Scala within the self-typed trait. I have something like this:
trait Foo
class FooImpl1 extends Foo
class FooImpl2 extends Foo
trait SpecialFoo {
this:Foo =>
def |+|(that:this.type):this.type // the type signature is incorrect here
}
where if I do new FooImpl1 with SpecialFoo, I'd like the |+| method to require and return a FooImpl1 (or a subtype of FooImpl1). However, with the above code it seems to want a SpecialFoo.this.type, which is unsurprising, but not what I want.
this.type is the singleton type of whatever instance of SpecialFoo you have. As defined, |+| would only be able to be called with itself. For example:
trait Spec { def |+|(that: this.type): this.type = that }
val s = new Spec {}
val s2 = new Spec {}
scala> s |+| s
res1: <refinement>.type = $anon$1#118102ee
scala> s |+| s2
<console>:14: error: type mismatch;
found : Spec
required: <refinement>.type
s |+| s2
^
this.type is FooImpl1 in some cases, but the compiler has no way of knowing that. You need some way to capture the more refined type of FooImpl1 or FooImpl2. The self-type this: Foo => only cares that it's a Foo. There are a couple possibilities, but neither will look as nice as you want.
You can parameterize SpecialFoo:
trait Foo
class FooImpl1 extends Foo
class FooImpl2 extends Foo
trait SpecialFoo[A <: Foo] { self: A =>
def |+|(that: A): A
}
val foo = new FooImpl1 with SpecialFoo[FooImpl1] {
def |+|(that: FooImpl1): FooImpl1 = that
}
Unforunately, you need to write FooImpl1 twice, but the self-type still prevents you from mixing two different implementations.
The alternative is to use type members within Foo. You wouldn't have to specify the implementation type twice when creating a SpecialFoo, but would when creating the implementations themselves to bind the correct types.
trait Foo { type S }
class FooImpl1 extends Foo { type S = FooImpl1 }
class FooImpl2 extends Foo { type S = FooImpl2 }
trait SpecialFoo { self: Foo =>
def |+|(that: self.S): self.S
}
val foo = new FooImpl1 with SpecialFoo {
def |+|(that: FooImpl1): FooImpl1 = that
}
You could also make Foo F-bounded, i.e. trait Foo[A <: Foo], and do something similar to the above example.

In Scala, Refer to Subclass in Abstract Super Class

Is there a way for the this keyword in a super class to refer to that class's subclass? Specifically, I am trying to do the following (the Json refers to Play's Json library):
abstract class A() {
def toJson[T](implicit writes: Writes[T]): JsValue = Json.toJson(this)
}
case class B(myProperty: String) extends A
object B { implicit val bFormat = Json.format[B] }
This gives the error No Json serializer found for type A. Try to implement an implicit Writes or Format for this type.. So it's saying it can't serialize an object of type A, which makes sense. The goal, however, is for the this in Json.toJson(this) to refer to the subclass (which, in this instance, is B).
Is there any way to accomplish this? If not, is there any other way I can implement the Json.toJson(...) method in the superclass without having to implement in the subclass (and all other subclasses of A)?
The common trick to refer to the current subclass from the parent, is to use F-bounded polymorphism:
// Here `T` refers to the type of the current subclass
abstract class A[T <: A[T]]() {
this: T =>
def toJson(implicit writes: Writes[T]): JsValue = Json.toJson(this)
}
// We have to specify the current subclass in `extends A[B]`
case class B(myProperty: String) extends A[B]
object B { implicit val bFormat = Json.format[B] }
println(B("foo").toJson)
This won't allow you to call toJson for any generic A though:
val a: A[_] = B("foo")
println(a.toJson) // Doesn't compile with:
// No Json serializer found for type _$1.
// Try to implement an implicit Writes or Format for this type.
To fix this you would have to save Writes for the subtype at the point of object creation:
abstract class A[T <: A[T]](implicit writes: Writes[T]) {
this: T =>
def toJson: JsValue = Json.toJson(this)
}
Or alternatively using the context bound notation:
abstract class A[T <: A[T] : Writes] {
this: T =>
def toJson: JsValue = Json.toJson(this)
}
And since this F-bounded polymorphism thing is just an implementation detail and always refering to a generic A as A[_] is quite boilerplate-y, you can move this code to an intermediate abstract class.
So a full example looks like this:
abstract class A() {
def toJson: JsValue
}
abstract class AImpl[T <: AImpl[T] : Writes] extends A {
this: T =>
def toJson: JsValue = Json.toJson(this)
}
case class B(myProperty: String) extends AImpl[B]
object B { implicit val bFormat: Format[B] = Json.format[B] }
val a: A = B("foo")
println(a.toJson)

Scala: illegal inheritance; self-type Foo[T] does not conform to Foo[T]'s selftype T

I have the following code snippet:
abstract class Foo[T <: Foo[T]] { self: T =>
def bar(x: T): T
def newFoo: Foo[T] = {
new Foo[T] { self: T =>
// ...
}
}
}
I have a need to generate a new instance of Foo within a method of my abstract class. Can anyone advise me on how best to approach this?
Thanks,
Hadil
The self-type self: T => implies that your Foo[T] must also be a T. new Foo[T] { ... } isn't an instance of T for any arbitrary T that makes up Foo[T]. You also can't add a self-type to an anonymous class like new Foo[T] { ... }, because it doesn't make sense. Either the concrete class is or isn't a T at that point.
Constructing a method like def newFoo: Foo[T] in a type-safe way isn't really possible with the self-type in place, because you'd need to know how to construct an arbitrary T. You might be able to do what you want with reflection, when each T has the same constructor.
import scala.reflect._
abstract class Foo[T <: Foo[T] : ClassTag] { self: T =>
def bar(x: T): T
def newFoo: Foo[T] = classTag[T].runtimeClass.newInstance.asInstanceOf[T]
}
class Bar extends Foo[Bar] {
def bar(x: Bar): Bar = x
}
scala> val b = new Bar
b: Bar = Bar#2a2d45ba
scala> b.newFoo
res1: Foo[Bar] = Bar#146ba0ac
This ceases to work when there are constructor parameters:
case class Baz(i: Int) extends Foo[Baz] {
def bar(x: Baz): Baz = x
}
scala> val baz = new Baz(0)
baz: Baz = Baz(0)
scala> baz.newFoo
java.lang.InstantiationException: Baz
at java.lang.Class.newInstance(Class.java:427)
at Foo.newFoo(<console>:16)
Well, you do not know the concrete class where Foo will be inherited in the future. So especially, you do not know which constructor parameters this class will have and how to supply arguments for them. If you really would want to do this, you would have to make some assumptions (that you cannot enforce at compile time) to achieve this.
So what you probably should do, is, leave this method abstract and implement it in a subclass. If this is not an option, you probably have some issues with your overall class design. Better present here what you want to model and ask for help.
If you assume, that the constructor of the concrete class won't have any parameters, you can implement newFoo just as
def newFoo = this.getClass.newInstance
There is no need for classTags or other fancy stuff.