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] = ???
}
Related
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!
I'm new to Scala and new to higher kinded types. I want to write something like this;
trait Actor[E[Dependency] <: Event[Dependency]] {
def execute(dependency: Dependency): Unit
}
However I can't refer to the type parameter Dependency in the execute method - the compiler doesn't know it.
I'm aware I can solve it in the following way without HKTs, but this isn't what this question is about;
trait Actor[T <: Event[Dependency], Dependency] {
def execute(dependency: Dependency): Unit
}
I'd like to understand why it doesn't work with the higher kinded type syntax that I've tried? Is it possible at all to express this with HKTs? Is this a valid use-case for a HKT?
EDIT
A bit more information, Event looks like this;
trait Event[Data] {
val payload: Data
}
...and I'm looking to define an event and an actor like this;
case class FooEvent(payload: Foo) extends Event[Foo]
class FooActor extends Actor[FooEvent] {
def execute(dependency: Foo) = {}
}
I will try to improve Alexey's answer - he is right, but he is too short. But I must say that I'm not an expert in HKT and I think I'm only starting to understand the concept.
In your code E[Dependency] is the same as E[_] which says that you have E with some type as parameter. This means that you do not operate over Dependency as type. You also do not operate over E or E[Dependency] as the type either. E is a type constructor, and E[Dependency] is an existential type if I understood it correctly. Please note that
trait Actor[E[D] <: Event[D]] { def execute(d: E) {} }
or
trait Actor[E[D] <: Event[D]] { def execute(d: E[D]) {} }
won't compile either.
You need to specify the proper type as an argument for execute:
trait Actor[E[D] <: Event[D]] { def execute[B](d: E[B]) {} }
This one will compile as E[B] is the type in this context.
Updated:
Please take a look at this code:
trait Event[P] {
val payload: P
}
case class FooEvent(payload: Int) extends Event[Int]
trait BaseActor {
type E = Event[P]
type P
def execute(dep: P)
def runEvent(event: E)
}
trait IntActor extends BaseActor {
type P = Int
}
class FooActor extends IntActor {
def execute(dependency: P) = {}
def runEvent(event: E) = {}
}
val c = new FooActor()
c.runEvent(FooEvent(5))
c.execute(5)
Basically the trick is to define type P which is our Dependency and type E = Event[P] which is always Event[Dependency] then you can use the actor by defining P without defining E as it is already defined. Not sure whether it solves the issue, but it looks like a way to go to me. There are also too many types here, some like IntActor is not necessary. I've put them so it is easier to understand the example
However I can't refer to the type parameter Dependency in the execute method - the compiler doesn't know it.
You can't because it isn't a parameter of Actor. Consider
val actor = new Actor[Event] // E is Event
actor.execute(???) // what argument is this supposed to take? I.e. what is Dependency for Actor[Event]?
UPDATE: Given your edit, the [Dependency, T <: Event[Dependency]] option is precisely what you need. When you write Actor[E[Dependency] <: Event[Dependency]], this means E itself has to have a type parameter. And FooEvent doesn't, so Actor[FooEvent] won't compile.
UPDATE 2: You could try using type members as follows:
trait Event {
type Dependency
val payload: Dependency
}
trait Actor {
type E <: Event
def execute(e: E#Dependency)
}
class Foo
case class FooEvent(payload: Foo) extends Event {
type Dependency = Foo
}
class FooActor extends Actor {
type E = FooEvent
def execute(e: Foo) = {}
}
Why does this not compile?
The error given is class SomeElement needs to be abstract, since method eval in trait Element of type [T <: Typed]=> scala.util.Try[T] is not defined
I cannot understand why the method eval defined on SomeElement does not satisfy the type constraints.
As I understand it, eval should return something wrapped in a Try which subclasses Typed.
Try is covariant in its type parameter. The implementation of eval in SomeElement returns a NumberLike, and NumberLike subclasses Typed. So what's gone wrong?
import scala.util.Try
trait Element {
def eval[T <: Typed]: Try[T]
}
trait Typed
case class NumberLike(n: Long) extends Typed
case class SomeElement(n: Long) extends Element {
def eval = Try {
NumberLike(n)
}
}
object app extends Application {
println (SomeElement(5).eval)
}
Trying to add an explicit type parameter to eval in SomeElement doesn't help either:
case class SomeElement(n: Long) extends Element {
def eval[NumberLike] = Try {
NumberLike(n)
}
}
Changing the defintion of SomeElement to the above gives:
found : <empty>.NumberLike
required: NumberLike(in method eval)
NumberLike(n)
EDIT I would really like to know why this doesn't compile. Workarounds for the problem are helpful, but I really want to know what's going on here.
The type parameter T being defined on the function, not on the enclosing type Element, it can't be 'erased' by inheritance and must be kept on the overriding function (http://www.scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#class-members).
Moving the type parameter to Element definition make it work as following.
trait Element[T <: Typed] {
def eval: Try[T]
}
trait Typed
case class NumberLike(n: Long) extends Typed
case class SomeElement(n: Long) extends Element[NumberLike] {
def eval = Try {
NumberLike(n)
}
}
Or using a type member:
trait Element {
type T <: Typed
def eval: Try[T]
}
trait Typed
case class SomeElement(n: Long) extends Element {
type T = NumberLike
def eval = Try {
NumberLike(n)
}
}
Since someone has given a solution, I'm trying to explain why.
In java, something like this is illegal:
class A {
<T> T f() {return null;}
Object f() {return null;}
}
But in scala, it's legal:
class A {
def f[T](): T = ???
def f(): Any = ???
}
The main difference is that: scala treat 2 methods with same name and parameters list different if one of them has type parameter and another don't.(I think the signature of scala method include type parameters, correct me if I was wrong :) )
So in your case, you can't overwrite a method with type parameter by a normal method.(they have different signatures)
I'm not sure which one is better, personally I prefer the scala rule :)
I have a generic type:
trait BaseTrait[T <: BaseTrait[T]] {
self: T =>
def similar(that: T): Float
}
A concrete type:
class MyConcrete(val..) extends BaseTrait[MyConcrete] {
type Self = MyConcrete
..
def similar(that: MyConcrete) = {
return 0.5f // dummy
}
}
I want to write something like this:
def create[T <: BaseTrait[T]](..): T = new MyConcrete(..)
I get the error:
type mismatch; found: MyConcrete required: T
I was expecting that since MyConcrete is a subtype of BaseTrait, it would work(but I am wrong)
My intention is to hide the construction of MyConcrete so that I can later change the above to MyConcrete2 (which also extends BaseTrait like MyConcrete) and my code which only cares about the BaseTrait interface will not be affected by my change.
How do I write the interface of my create method.
Thanks.
def create[T <: BaseTrait[T]](..): T = new MyConcrete(..)
That type parameter T doesn't make much sense. You are asking the caller to tell the method what T is, which obviously doesn't work in this scenario. If you were to interpret T as something given back from the method (automatically inferred), then still the caller couldn't do much with it.
From the caller's perspective the best you can get is
def create(..): BaseTrait[_] = new MyConcrete(..)
If on the other hand you want a specific subtype but hide the implementation of MyConcrete, you would define a trait
trait ConcreteLike extends BaseTrait[ConcreteLike]
class MyConcrete extends ConcreteLike {
def similar(that: ConcreteLike) = 0.5f
}
def create(): ConcreteLike = new MyConcrete()
I'm trying to achieve F-bounded polymorphism without using generics. I also need to use self-typing as I will be referencing this and expecting it to be typed as the subtype.
trait MyTrait[T] { self: Self => // Compilation error: cannot reference 'Self'
type Self <: MyTrait[T]
def doSomethingWithSubtype() {
...
}
}
I can achieve this quite easily using type parameters (i.e. generics), but would like to know if I'm missing something to make the above compile. Can you use abstract types in this way?
Similar questions:
These provide workarounds for similar problems, leading me to believe the above is impossible?
F-Bound Polymorphism with Abstract Types instead of Parameter Types?
F-bounded quantification through type member instead of type parameter?
You can self-type to an abstract type, with one tricky limitation: it has to be defined outside of your trait, but still in scope in a way that allows implementations to implement it with some type. You can do that by wrapping the whole thing into a trait:
trait MyTraitSystem {
type TraitImpl <: MyTrait
trait MyTrait { self: TraitImpl =>
def doSomething(t: TraitImpl): String
}
}
// with an example implementation of the trait:
object MyImpl extends MyTraitSystem {
case class TraitImpl(data: String) extends MyTrait {
def doSomething(t: TraitImpl): String = t.data + " " + data
}
}
This is equivalent to this version using a type parameter:
trait MyTrait[T <: MyTrait[_]] { self: T =>
def doSomething(t: T): String
}
// with an example implementation of the trait:
case class TraitImpl(data: String) extends MyTrait[TraitImpl] {
def doSomething(t: TraitImpl): String = t.data + " " + data
}
Apart from an import MyImpl._ for the abstract-type version, they can be used the same way:
scala> import MyImpl._
import MyImpl._
scala> val a = TraitImpl("hello")
a: MyImpl.TraitImpl = TraitImpl(hello)
scala> val b = TraitImpl("world")
b: MyImpl.TraitImpl = TraitImpl(world)
scala> b.doSomething(a)
res0: String = hello world
The abstract-type version is more verbose, but it works. You also need to carry around a MyTraitSystem in any method/class/... that needs to use TraitImpl so as to provide the type:
object SomewhereElse {
def doSomethingElse(s: MyTraitSystem)(t: s.TraitImpl) =
??? // s.TraitImpl is the implementation type
}
Compared to the type parameter version:
object SomewhereElse {
def doSomethingElse[T <: MyTrait[_]](t: MyTrait[T]) =
??? // T is the implementation type
}
This is probably only one of several ways to do this, but I don't think any way can match the conciseness of the type-parameter-based version.