Suppose you have a class Foo and two additional implicit classes:
implicit class RichFoo1(val foo: Foo) extends AnyVal {
def doSomething: Bar
}
implicit class RichFoo2(val foo: Foo) extends AnyVal {
def doSomething: Baz
}
Now, if a class has imported both RichFoo1 and RichFoo2, it seems that there is a likelihood of a collision when doSomething() is called, and the compiler may complain due to the ordering between the imports of the two implicit classes and/or information about the relationship between Bar and Baz.
I'm considering a workaround in the manner of the following:
trait ConvertibleFromFoo[T] {
def doSomething[T]: T
}
implicit class RichFooAs(val foo: Foo) extends AnyVal {
def doSomething[T](implicit f: ConvertibleFromFoo[T]): T = f.doSomething
}
implicit val bar: ConvertibleFromBoo[Bar]
implicit val baz: ConvertibleFromBaz[Baz]
The idea would be that foo.doSomething[Bar] would (with a bit of code) do what RichFoo1(foo).doSomething would be doing, for example.
What would be the drawbacks with regards to this approach here, as opposed to, say, explicit ascription?
Related to this: when would it be appropriate to outright define an implicit conversion method over defining an implicit class? That is:
implicit def toBar(foo: Foo): Bar
versus
implicit class RichFoo(val foo: Foo) extends AnyVal {
def toBar: Bar
}
(For example: scala-java8-compat uses the latter for converting between Option and Optional)
Related
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]
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.
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.
I'm doing an exercise to implement a functional binary-search-tree in Scala, following a similar pattern that I've seen used in Haskell. I have a structure that looks something like this:
trait TreeNode[A] {
def isLeaf: Boolean
def traverse: Seq[A]
...
}
case class Branch[A](value: A, left: TreeNode[A], right: TreeNode[A]) extends TreeNode[A] {
def isLeaf: Boolean = false
def traverse: Seq[A] = ...
...
}
case class Leaf[A]() extends TreeNode[A] {
def isLeaf: Boolean = true
def traverse: Seq[A] = Seq[A]()
...
}
I'd like to put a type constraint on A so that it will only accept objects that extend Ordered. It looks like I need to define a view bound on A ([A <% Ordered[A]]) on Branch and Leaf, as well as the TreeNode trait.. I can't do this on the TreeNode trait, however, because view bounds aren't accepted.
As I understand, <%-style view-bounds are syntactic sugar for an implicit definition, so there should be a way to write to define the bound manually within the TreeNode trait. I'm not sure how I'm supposed to do this, though. I've looked around a bit, but haven't gotten much further than that some sort of implicit needs to be defined.
Can anybody point me in the right direction? Am I approaching this from the wrong angle entirely?
The problem is that view bounds as well as context bounds are just syntactic sugar for specific types of implicit parameters. When applied to a type parameter of a generic class (as opposed to when applied to a generic method), these implicits are added to the constructor of the class.
Because traits have no constructor (or rather, only have a single parameterless constructor), there is nowhere to pass these implicit parameters and thus context bounds and view bounds are illegal on generic traits.
The simplest solution would be to turn TreeNode into an abstract class.:
abstract class TreeNode[A <% Ordered[A]]
Note that as advised by Ben James, using a context bound with an Ordering is usually better than a view bound with an Ordered (it is more general). However the problem is still the same: won't work on a trait.
If turning TreeNode into a class is not practical (say you need to mix it at various places in the type hierarchy), you can define an abstract method in TreeNode that will provide the implicit value (of type Ordered[A]) and have all the classes that extend it define it. This unfortunately more verbose and explicit, but you can't do much better in this case:
trait TreeNode[A] {
implicit protected def toOrdered: A => Ordered[A]
}
case class Branch[A<%Ordered[A]](value: A, left: TreeNode[A], right: TreeNode[A]) extends TreeNode[A] {
protected def toOrdered = implicitly[A => Ordered[A]]
}
case class Leaf[A<%Ordered[A]]() extends TreeNode[A] {
protected def toOrdered = implicitly[A => Ordered[A]]
}
Note that for a more concise definition, you could equivalently define Leaf like this:
case class Leaf[A](implicit protected val toOrdered: A => Ordered[A]) extends TreeNode[A]
You could provide the "evidence" that A is Ordered by requiring an abstract member of type Ordered[A] on the trait:
trait TreeNode[A] {
implicit val evidence: Ordered[A]
}
You would then be forced to provide this in any concrete subtypes, this proving that A is Ordered:
case class Leaf[A](value: A)(implicit ev: Ordered[A]) extends TreeNode[A] {
val evidence = ev
}
You might instead want to constrain A to a type which has an implicit Ordering[A] - this is not an inheritance relationship; it is more like a haskell typeclass. But the implementation in terms of the above technique would be the same.
#ben-james's answer is great, I would like improve it a bit to avoid redundant vals in classes.
The idea is to define implicit constructor parameter name the same as it is defined in trait that holds implicit value.
The idea is to avoid this line:
val evidence = ev
Here is a complete example (gist)
trait PrettyPrinted[A] extends (A => String)
object PrettyPrinted {
def apply[A](f: A => String): PrettyPrinted[A] = f(_)
}
trait Printable[A] {
implicit def printer: PrettyPrinted[A]
}
// implicit parameter name is important
case class Person(name: String, age: Int)
(implicit val printer: PrettyPrinted[Person])
extends Printable[Person]
object Person {
implicit val printer: PrettyPrinted[Person] =
PrettyPrinted { p =>
s"Person[name = ${p.name}, age = ${p.age}]"
}
}
// works also with regular classes
class Car(val name: String)
(implicit val printer: PrettyPrinted[Car])
extends Printable[Car]
object Car {
implicit val printer: PrettyPrinted[Car] =
PrettyPrinted { c =>
s"Car[name = ${c.name}]"
}
}
I'm very new to Scala so forgive me if this is a real easy question but I could not find anything to help me or I could not figure out the right search terms. How can I make this work?
scala> trait Foo
defined trait Foo
scala> class FooImpl extends Foo
defined class FooImpl
scala> trait Bar { def someMethod(foo: Foo) }
defined trait Bar
scala> class BarImpl extends Bar { def someMethod(foo: FooImpl) {} }
<console>:10: error: class BarImpl needs to be abstract, since method someMethod in trait Bar of type (foo: Foo)Unit is not defined
(Note that Foo does not match FooImpl)
class BarImpl extends Bar { def someMethod(foo: FooImpl) {} }
Why doesn't FooImpl match Foo since Foo is a trait? I'm guessing I need to alter the signature of someMethod in Bar to say that I'm expecting something that extends Foo or "with Foo" but I can't seem to find documentation for this.
The problem is that the Bar trait's someMethod declaration specifies that any kind of Foo can be passed as an argument. You can think of this as its "contract". The contract says that any implementation of Bar will have a method someMethod that will accept any kind of Foo.
Your BarImpl class is an implementation of Bar and has a someMethod implementation. Unfortunately, its implementation of someMethod only accepts FooImpl kinds of Foo objects: not any kind of Foo. Since it doesn't allow you to pass in Foo objects that aren't FooImpl objects, it violates the contract specified by the trait definition. Implementations can't be more restrictive than the contract specifies.
As an example:
class FooImplB extends Foo
val bar: Bar = new BarImpl
val foo: Foo = new FooImplB
bar.someMethod(foo)
Here we declare a Bar called bar and a Foo called foo. According to the definition of Foo I should be able to pass foo into bar.someMethod. Except that BarImpl.someMethod only accepts FooImpl kinds of Foos and not FooImplBs! So we have a problem.
dhg explained why this doesn't work and why you probably don't really want it.
But if you still want it, you can do it like this:
trait Foo
class FooImpl extends Foo
trait Bar[F <: Foo] { def someMethod(foo: F) }
class BarImpl extends Bar[FooImpl] {
def someMethod(foo: FooImpl) {}
}
Jens Schauder's answer works but forces you to define the type in the trait signature. Instead, you can do the same on the method level:
scala> trait Foo
defined trait Foo
scala> class FooImple extends Foo
defined class FooImple
scala> trait Bar { def methodA[T <: Foo](foo: T) }
defined trait Bar
scala> class BarImpl extends Bar { def methodA[FooImpl](foo: FooImpl){} }
defined class BarImpl