I have a generic class that looks like:
class GenericClass[T <: AnyRef] {
def getHash(obj: T): String = obj.toString
}
As you can see, type T needs to have implemented the toString function in order for getHash to work properly. My question: is that possible to apply type bound/constraints so that type T always have toString implemented?
One way that I can think of is to use type class and context bound:
class GenericClass[T : ClassWithToString] {...}
trait ClassWithToString[T] {
def toString(t: T): String
}
implicit object SomeTypeWithToString extends ClassWithToString[SomeType] {
override def toString(a: SomeType): String = a.toString()
}
However, this approach requires clients to define new implicit objects whenever they want to use GenericClass with a new type, which is not ideal in my case. Especially given toString is a very common function that's being implemented by many types. Wanted to get some advice from you on how to solve this issue elegantly!
Related
i am trying to do something like this in scala so that the Category class receives its attributes by parameters but i get the following error:
object creation impossible, since method apply in trait ModelCompanion of type => asd.Category is not defined
object Category extends ModelCompanion[Category] {
^
one error found
Code here:
object asd {
trait ModelCompanion[M <: Model[M]] {
def apply: M
}
trait Model[M <: Model[M]] {
var id: Int = 0
}
object Category extends ModelCompanion[Category] {
def apply(name: String): Category = new Category(name)
}
class Category(var name: String) extends Model[Category] {
// Do something with name
}
}
I am new to scala so if you could give me some guidance on this I would be very grateful.
ModelCompanion defines an abstract method apply without any arguments (or argument lists). In Category you define an apply method that takes an argument of type String. That's not an implementation of the abstract method because it doesn't accept the same number and types of arguments. Therefore Category does not provide a suitable definition of ModelCompanion's abstract apply method and therefore can not be instantiated.
Depending on the behavior you want, you should either change the definition of ModelCompanion.apply to def apply(name: String): M or introduce another type argument and use that as the argument type.
Shortly:
def apply:M
//and
def apply(name:String):M
//are not the same methods
//if you try define it with override modifier
override def apply(name: String): Category = new Category(name)
//it will expose this fact to you with error:
//method apply overrides nothing.
//Note: the super classes of object Category
// contain the following, non final members named apply:
//def apply: ammonite.$sess.cmd8.asd.Category
//You need to define `ModelCompanion` with appriopriate `apply`
trait ModelCompanion[M <: Model[M]] {
def apply(name:String): M
}
// or override properly current (argumentless) one
object Category extends ModelCompanion[Category] {
override def apply: Category = new Category("Category" + Random.nextInt())
}
Let's imagine I have the following base trait and case classes
sealed trait BaseTrait
case class Foo(x: Integer = 1) extends BaseTrait
case class Bar(x: String = "abc") extends BaseTrait
I would like to create a generic interface for classes which can process BaseTrait instances, something like the following
class FooProcessor(val model: FooModel) extends BaseProcessor[Foo] {
val v: Option[Foo] = model.baseVar
}
class BarProcessor(val model: BarModel) extends BaseProcessor[Bar] {
val v: Option[Bar] = model.baseVar
}
For this I have the following traits
trait BaseModel[T <: BaseTrait] {
var baseVar: Option[T] = None
}
trait BaseProcessor[T <: BaseTrait] {
def model: BaseModel[T]
def process(x: T): Unit = model.baseVar = Option(x)
}
The model definitions are the following
class FooModel extends BaseModel[Foo]
class BarModel extends BaseModel[Bar]
Now lets imagine I have the following processors somewhere in my app
val fooProcessor = new FooProcessor(new FooModel)
val barProcessor = new BarProcessor(new BarModel)
I would like to handle them in a somewhat generic way, like this
def func[T <: BaseTrait](p: T) {
val c/*: BaseProcessor[_ >: Foo with Bar <: BaseTrait with Product with Serializable]*/ = p match {
case _: Foo => fooProcessor
case _: Bar => barProcessor
c.process(p)
}
The compiler is not really happy about the last line, it says
type mismatch;
found : T
required: _1
If I understand correctly this is basically the compiler trying to prevent barProcessor.process(Foo()) from happening. I've tried a couple of solutions to get around this and achieve the desired behavior:
the simplest way around this is calling the proper *Processor.process with the proper BaseTrait instance inside the match case, which seems to defy the whole point of handling them in a somewhat generic way
use an abstract type in the BaseModel and BaseProcessor, which one hand got rid of the somewhat unneeded type parameter in BaseModel but the compilers complaint is still valid and I was not able to figure out if it's possible to get that to work
get rid of the type parameter and contraint from the BaseModel, and just do a type cast in the processor to get the proper type, but the explicit type cast also isn't really what I was hoping for
Like so:
trait BaseModel {
var baseVar: Option[BaseTrait] = None
}
trait BaseProcessor[T <: BaseTrait] {
def model: BaseModel
def process(x: T): Unit = model.baseVar = Some(x)
def getBaseValue: T = model.baseVar.map(_.asInstanceOf[T])
}
I guess one could also somehow convince the compiler that the two types (T of the Processor and T of the func parameter p) are equivalent, but that also seems like an overkill (and I'm also not really sure how it can be done).
So my question is the following: is it possible to do what I'm trying to achieve here (managing processors in a uniform way + each processor knows their specific type of BaseTrait) in a somewhat easy fashion? Is there a better model for this which I'm missing?
Update
As per Tim's answer making the controllers implicit solves the problem, however if you want to have a class where you define your controllers + have 'func' on it's interface the compiler no longer seems to properly resolve the implicits. So if I try to do something like this
class ProcessorContainer {
implicit val fooProcessor = new FooProcessor(new FooModel)
implicit val barProcessor = new BarProcessor(new BarModel)
def func[T <: BaseTrait](p: T) = typedFunc(p)
private def typedFunc[T <: BaseTrait](p: T)(implicit processor: BaseProcessor[T]) =
processor.process(p)
}
class Test {
val processorContainer = new ProcessorContainer
processorContainer.func(Foo())
processorContainer.func(Bar())
}
I get the following compile error (one for Foo and one for Bar):
could not find implicit value for parameter processor: BaseProcessor[Foo]
not enough arguments for method
Is there a way around this? I could of course expose the controllers so they can be passed in implicitly, however I'd prefer not doing that.
You can create a simple typeclass by making the processors implicit and passing them as an extra argument to func:
implicit val fooProcessor = new FooProcessor(new FooModel)
implicit val barProcessor = new BarProcessor(new BarModel)
def func[T <: BaseTrait](p: T)(implicit processor: BaseProcessor[T]) =
processor.process(p)
If you pass a Foo to func it will call FooProcessor.process on it, and if you pass a Bar to func it will call BarProcessor on it.
When you have a parent:
abstract class Parent {
def something(arg: ???): Parent = ???
}
and
class Child extends Parent {}
I would like
val updatedChild = new Child().something(...)
updatedChild to be of type Child and not of type Parent, is it possible ?
One way to do it, is to parametrize the parent:
abstract class Parent[T <: Parent[T]] {
def something(arg: Foo): T
}
class Child(val foo: String) extends Parent[Child] {
def something(arg: String) = return new Child(arg)
}
Sometimes, you can also get away with using this.type:
class Parent {
def something(arg: Foo): this.type = this
}
class Child {
override def something(arg: Foo) = this
}
But the latter method only works if all you ever want to return is this (this.type is not Parent or Child, but a specific type that only has one instance - this).
Here is a proposal that actually compiles:
abstract class Parent[Repr <: Parent[Repr]] {
def something(arg: Int): Repr
}
This is something you can do, at least it's not explicitly discouraged. Standard collection library uses it a lot, see e.g. IterableLike as a typical example of such F-bounded polymorphism.
It seems that you can do :
class Parent[THIS <: Parent[THIS]] {
def something: THIS
}
And that seems to work.
I am not sure if this is something you should do tho.
Both Andrey's and Dima's answers cover one way to solve the problem using only oo-patterns.
However I would like to point out another approach called typeclasses (which is more common in functional languages), that would be helpful if you are planning to write generic functions using your interface.
First, instead of having a parent class, you have an interface that describes the operations that can be performed on instances of the typeclass.
trait Typeclass[T] {
def something(t: T)(arg: Foo): T
}
Then, you would define your types, this time they don't extend any parent class, thus they don't have to override nothing.
class Child {
...
}
Now, you have to prove that your type is an instance of the type class.
(A common place to do that is in the companion object of the class).
object Child {
implicit final val ChildTypeclass: Typeclass[Child] = new Typeclass[Child] {
override def something(child: Child)(arg: Foo): Child = ???
}
}
Finally, you define a generic method that can operate on any type T as long as there is an instance of your typeclass for that type.
def generic[T](t: T, arg: Foo)(implicit tt: Typeclass[T]): T =
tt.something(t)(arg)
Bonus, if you want to recover the "dot notation" you can add an Ops pattern to your Typeclass.
object syntax {
object typeclass {
implicit final class TypeclassOps[T](val t: T) extends AnyVal {
final def something(arg: Foo)(implicit tt: Typelcass[T]) =
tt.something(t)(arg)
}
}
}
import syntax.typeclasss._
def generic[T: Typelcass](t: T, arg: Foo): T
t.something(arg)
val newChild = generic(new Child, new Foo)
// newChild: Child = ???
Also, a common approach is to define the something method in your class and the typeclass instance forwards the call to the one defined in the class, this way you can use your method in any instance of Child without having to put all the typeclass machinery.
I must say that this is useful for very high-level abstractions to which you plan to provide instances for many types (even types outside your control like any of the standard collection types) and write very generic functions that can operate on any of these.
If not, F-bounded types seems like the more rational solution.
I have a trait that defines an action which is a glorified version of .copy(). which looks like this:
trait Optimize[T] {
def optimize: T
}
and a bunch of classes that extend it like:
case class Account(field: String) extends Optimize[Account] {
def optimize = this.copy(field = field.intern())
}
Is there a way to define a trait that requires a method optimize return the same type as self but doesn't require specifying a type when extending it? So I'd be able to write:
case class Account(field: String) extends Optimize {
def optimize = this.copy(field = field.intern())
}
You can use this.type as return value to refer to the implementor's type.
def optimize: this.type = ???
Short answer: You can't.
One way or another (abstract types) you need to tell Optimize what the return type of that function is.
Why? Because Optimize can be used in a type expression without specifying the concrete class, and there's no way for the compiler to know what type it will produce:
def someOtherMethod (a: Optimize) {
val result = a.optimize // what is the type?
}
(in the case of an abstract type, the return type would be a.T or Optimize#T ... not very useful)
If all you want is to avoid specifying a type parameter when extending, you can move the definition to a type member like this.
trait Optimize {
type T
def optimize: T
}
case class Account(field: String) extends Optimize {
type T = Account
def optimize = this.copy(field = field.intern())
}
But you just moved it from one place to another. Also, it's much better practice to parameterize the trait with a type parameter than to get type members involved.
What is the reason for doing this? Because your use case seems like a perfectly valid situation for a type parameter. Trait needs to be told what it is that optimize() returns, either via type parameter or type member.
Something like this?
trait Ret[T]
trait A {
def func(): Ret[_ <: A]
}
case class B() extends A {
override def func(): Ret[B] = ???
}
I'm looking for a way to define a method that returns a type T where T = the type of the subclass.
I know I could possibly do this using abstract types, but dislike the overhead of having to redefine T for each subclass.
Some sample code:
object Helper {
def help[A <: MyClass](cls: A): Option[A] = { cls.foo() map { _.asInstanceOf[A] } }
}
class MyClass {
type T <: MyClass
def foo(): Option[T] = Some(this.asInstanceOf[T])
}
class ChildClass extends MyClass {
type T = ChildClass
}
Possibly a new language feature has made this easier? Or can I use this.type in some way? It's important to me that I be able to define a helper class that can call into foo in this way.
If you are always returning this, then you can indeed have as return type this.type. Or have you tried it already?
this.type is especially useful e.g. when you want to chain calls to the same object, or provide a static guarantee that you will be returning the same object (and not a copy). For instance, Buffers in Scala have the append operation :+, which returns a Buffer[A], and +=, which returns this.type. The former duplicates the mutable sequence; the latter guarantees that you update the original object.
To follow up on Jean-Phillippe's answer, who wrote his exactly when I'm writing mine, here's the code:
trait SomeTrait {
def foo: this.type = this
}
class UsesTrait extends SomeTrait
object Main {
def main(args: Array[String]) {
println((new UsesTrait).foo) // prints UsesTrait#<hash value>
}
}
I found the following idiom useful:
class MyClass[T] {
self: T =>
def foo(): Option[T] = Some(this)
}
class ChildClass extends MyClass[ChildClass]
new ChildClass().foo()
//--> Option[ChildClass] = Some(ChildClass#2487b1)