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) = {}
}
Related
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.
I am new to Scala and still wrapping my head around the type system and how flexible it is / is not. I'm having some issues understanding some of the type scoping and what type parameters I can and cannot refer to when defining parameters in various locations - especially with "higher kinded types" where I'm nesting types at two levels, ie: Foo[T <: Bar[U]](x: U) // U does not seem to be accessible here as a type for x
What I'd like to be able to do is define a Config type which is typesafe but generic enough to be used for multiple types (Config[T]), such that I can pass in Config[T](c: Map[AttributeKey[Attribute[T]], AttributeValue[Attribute[T]]]) which enforces that only AttributeKeys for T can be passed in, and, similarly, only AttributeValues fora specific Attribute for T.
What I have defined below does not work, and I have a feeling I may be going down the wrong path. I would really love if someone could help me detangle it and understand it a bit more. I've tried taking a look at the SBT source, but haven't been able to distill it down to the parts that I need.
package foo.conf
package object conf {
case object Provider {
sealed trait Type {
def name: String
override def toString = name
}
sealed trait MyProvider extends Type {
val name = "MyProvider"
}
}
import Provider._
case class Provider[T <: Type](config: Config[T])
case object Config {
sealed trait Attribute[T <: Type, VT] {
def name: String
def value: VT
override def toString = name
}
final case class AttributeKey[T <: Attribute](name: String)
case object AttributeKey {
implicit def attributeToKey[T <: Attribute](a: T): AttributeKey[T] = AttributeKey(a.name)
}
final case class AttributeValue[VT, T: Attribute[_, VT]](value: VT)
case object AttributeValue {
implicit def attributeToValue[T](a: Attribute[_, T]): AttributeValue[T] = AttributeValue(a.value)
}
case class MyProviderSpecificAttr[T: String](value: T) extends Attribute[MyProvider, T] {
val name = "my-provider-specific-attr"
}
case object `my-provider-specific-attr-val-1` extends MyProviderSpecificAttr("my-provider-specific-attr-val-1")
case object `my-provider-specific-attr-val-2` extends MyProviderSpecificAttr("my-provider-specific-attr-val-2")
}
import Config._
final case class Config[T <: Type](c: Map[AttributeKey[Attribute[T, _]], AttributeValue])
val test = Config[MyProvider](Map(
MyProviderSpecificAttr -> `my-provider-specific-attr-val-1`
))
}
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 some simple traits (Entity in the example below) that are extended by case classes in my app. I would like to create an EntityMapper trait that provides an interface for handling the case classes that extend the Entity trait (Foo in the example below). I thought I should be able to do this fairly easily using generics and bounding but I've spent a couple of hours on it already and I haven't gotten it to work correctly. The code below is what I think I should be able to do but it fails with a compiler error. The error is
Test.scala:15: error: value id is not a member of type parameter Foo \
println(e.id)
package experiment
trait Entity {
val id: Option[Long]
}
case class Foo(val id: Option[Long] = None) extends Entity
trait EntityMapper {
def create[E <: Entity](e: E): E
}
object FooMapper extends EntityMapper {
def create[Foo](e: Foo): Foo = {
println(e.id)
e
}
}
object Main extends App {
val foo = FooMapper.create(Foo(None))
}
I've tried several different things to solve the problem but nothing has worked. If I comment out the line in question "println(e.id)", it compiles but that is not useful because I cannot access or modify any of the properties of Foo.
I have tried using a covariant argument to the mapper trait and then supplying the type to the FooMapper object definition but that yields the same error. The code for that attempt is below:
trait EntityMapper[+Entity] {
def create[E <: Entity](e: E): E
}
object FooMapper extends EntityMapper[Foo] {
...
}
I have also tried achieving the same thing with simple inheritance but I cannot correctly restrict the type parameter in FooMapper to only take Foos, I have to make the method signature match the trait exactly which is why I started trying to implement it using generics with a type bound. The code for that attempt is below:
trait EntityMapper {
def create(e: Entity): Entity
}
object FooMapper extends EntityMapper {
def create(e: Foo): Foo = {
println(e.id)
e
}
}
The error code returned is:
Test.scala:13: error: object creation impossible, since method create in trait EntityMapper of type (e: experiment.Entity)experiment.Entity is not defined
(Note that experiment.Entity does not match experiment.Foo: class Foo in package experiment is a subclass of trait Entity in package experiment, but method parameter types must match exactly.)
object FooMapper extends EntityMapper {
^
Any help would be greatly appreciated. I'm using Scala version 2.10.3.
You can fix the error in a couple of ways
1.Specifying the generic type constraint on the trait.
trait EntityMapper[E <: Entity] {
def create(e: E): E
}
object FooMapper extends EntityMapper[Foo] {
def create(e: Foo): Foo = {
println(e.id)
e
}
}
2.Use parameterized types
trait EntityMapper {
type E <: Entity
def create(e: E): E
}
object FooMapper extends EntityMapper {
type E = Foo
def create(e: Foo): Foo = {
println(e.id)
e
}
}
Look at Scala: Abstract types vs generics to get some more background on the two approaches.
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.