I have the following class:
abstract class IRDMessage extends Ordered[IRDMessage] {
val messageType: MessageType.Type
val timestamp: DateTime
val content: AnyRef
def compare(that: IRDMessage): Int = {
val res = timestamp compareTo that.timestamp
res
}
override def equals(obj: Any): Boolean = obj match{
case message: IRDMessage => compareTo(message) == 0
case _ => false
}
}
I have a couple of concrete implementations as well. However, when I try to say a == b of any subtype of IRDMessage the equals method does not get called and it simply compares references (default equals implementation). Any ideas what might be causing this?
The subclasses are simple case classes btw.
This does actually work given the following simplified example:
object MessageTest {
def main(args: Array[String]) {
val m1 = MessageImpl("foo")
val m2 = MessageImpl("bar")
m1 == m2
}
}
abstract class IRDMessage extends Ordered[IRDMessage] {
val content: AnyRef
override def equals(obj: Any): Boolean = {
println("in here")
obj match{
case message: IRDMessage => compareTo(message) == 0
case _ => false
}
}
}
case class MessageImpl(content:String) extends IRDMessage{
override def compare(that:IRDMessage) = 0
}
Can you post a little more code, specifically one of your sample case classes? I noticed if I defined the case class like so:
case class MessageImpl extends IRDMessage{
var content = "foo"
override def compare(that:IRDMessage) = 0
}
It does not work as expected.
Related
I have an array of Any (in real life, it's a Spark Row, but it's sufficient to isolate the problem)
object Row {
val buffer : Array[Any] = Array(42, 21, true)
}
And I want to apply some operations on its elements.
So, I've defined a simple ADT to define a compute operation on a type A
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
Given that I have a list of all operations and I know which operation is to apply to each element, let's use these operations.
object GenericsOp {
import Row._
val ops = Seq(Count, Exist)
def compute() = {
buffer(0) = ops(0).compute(ops(0).cast(buffer(0)))
buffer(1) = ops(0).compute(ops(0).cast(buffer(1)))
buffer(2) = ops(1).compute(ops(1).cast(buffer(2)))
}
}
By design, for a given op, types are aligned between cast and combine. But unfortunately the following code does not compile. The error is
Type mismatch, expected: _$1, actual: AnyVal
Is there a way to make it work ?
I've found a workaround by using abstract type member instead of type parameter.
object AbstractOp extends App {
import Row._
trait Op {
type A
def compute(a: A) : A
}
case object Count extends Op {
type A = Int
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op {
type A = Boolean
override def compute(a: Boolean): Boolean = a
}
val ops = Seq(Count, Exist)
def compute() = {
val op0 = ops(0)
val op1 = ops(1)
buffer(0) = ops(0).compute(buffer(0).asInstanceOf[op0.A])
buffer(1) = ops(0).compute(buffer(1).asInstanceOf[op0.A])
buffer(2) = ops(1).compute(buffer(2).asInstanceOf[op1.A])
}
}
Is there a better way ?
It seems that your code can be simplified by making Op[A] extend Any => A:
trait Op[A] extends (Any => A) {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
def apply(a: Any): A = compute(cast(a))
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i)(buffer(i))
}
println(buffer.mkString("[", ",", "]"))
}
}
Since it's asInstanceOf everywhere anyway, it does not make the code any less safe than what you had previously.
Update
If you cannot change the Op interface, then invoking cast and compute is a bit more cumbersome, but still possible:
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i) match {
case op: Op[t] => op.compute(op.cast(buffer(i)))
}
}
println(buffer.mkString("[", ",", "]"))
}
}
Note the ops(i) match { case op: Opt[t] => ... } part with a type-parameter in the pattern: this allows us to make sure that cast returns a t that is accepted by compute.
As a more general solution than Andrey Tyukin's, you can define the method outside Op, so it works even if Op can't be modified:
def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))
buffer(0) = apply(ops(0), buffer(0))
sorry if the title is not very explicative, but I don't know how to explain it. I'm a scala newbie and I'm struggling in finding a solution to my problem. Here's the snippet:
sealed trait Base[T, M] {
var value: Option[T] = None
def initialize(v: T): this.type = {
value = Some(v)
this
}
def combine[K <: M](other: K): M
}
class Child1 extends Base[String, Child1] {
override def combine[K <: Child1](other: K) = {
val c = new Child1()
c.initialize(value.get + " - " + other.value.get)
c
}
}
class Child2 extends Base[Long, Child2] {
override def combine[K <: Child2](other: K) = {
val c = new Child2()
c.initialize(value.get + other.value.get)
c
}
}
object Main extends App {
val c1a = new Child1()
c1a.initialize("a")
val c1b = new Child1()
c1b.initialize("b")
val c21 = new Child2()
c21.initialize(1)
val c22 = new Child2()
c22.initialize(2)
val m1 = Map("child1" -> c1a, "child2" -> c21)
val m2 = Map("child1" -> c1b, "child2" -> c22)
m1("child1").combine(m2("child1"))
}
What I want to achieve is that each subclass of Base can be combined only with objects of the same type.
The compiler complains when calling the combine method due to a mismatch in the type of the argument. Is this a correct approach? Or the structure of the classes for my purpose is to be rewritten?
EDIT
This should be ok as well:
sealed trait Base[T, M] {
var value: Option[T] = None
def initialize(v: T): this.type = {
value = Some(v)
this
}
def combine(other: M): M
}
class Child1 extends Base[String, Child1] {
override def combine(other: Child1) = {
val c = new Child1()
c.initialize(value.get + " - " + other.value.get)
c
}
}
class Child2 extends Base[Long, Child2] {
override def combine(other: Child2) = {
val c = new Child2()
c.initialize(value.get + other.value.get)
c
}
}
CURRENT SOLUTION
The solution I found so far:
val combined = (m1("child1"), m2("child1")) match {
case (a: Child1, b: Child1) => a.combine(b)
case _ => throw new Error("Error")
}
What I want to achieve is that each subclass of Base can be combined
only with objects of the same type.
Instead of using pattern match on (m1("child1"), m2("child1")) you can directly use type check on combine method on each child class. In addition, your code seem more imperative style, such as using var, I have refractored your code in more functional way.
sealed trait Base[T] {
val value: Option[T] = None
def combine[Other](other: Base[Other]): Base[T]
}
case class Child1(override val value: Option[String]) extends Base[String] {
override def combine[Other](other: Base[Other]) = {
other match {
case v: Child1 => this.copy(v.value)
case _ => throw new Error("Error")
}
}
}
case class Child2(override val value: Option[String]) extends Base[String] {
override def combine[Other](other: Base[Other]) = {
other match {
case v: Child2 => this.copy(v.value)
case _ => throw new Error("Error")
}
}
}
val child1 = Child1(Some("child1"))
val child2 = Child2(Some("child2"))
child1.combine(child2) //Will fail
val anotherChild1 = Child1(Some("Another child1"))
child1.combine(anotherChild1) //Will succeed.
I have this class that manage reception of a stream of asynchronous messages. It has type argument, say A, and I like to implement a function that filter those messages by type, say B<:A
But my first implementation is not working due to type erasure (see example below). Is there a good way to so?
Here is a simplified example of my problem:
package test.filterByType
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.Future
trait MessageStream[A]{ self =>
def next(): Future[A]
def filter[B<:A]: MessageStream[B] = new MessageStream[B]{
def next(): Future[B] = self.next.flatMap{
case b: B => Future.successful(b)
case _ => next()
}
}
}
case class MessageStreamImpl[A](msg: IndexedSeq[A]) extends MessageStream[A]{
private var index = 0
def next() = {
index += 1
Future.successful(msg(index-1))
}
}
object Main{
trait A
case class B(i: Int) extends A
case class C(i: Int) extends A
def main(args: Array[String]){
val msg: IndexedSeq[A] = (1 to 10).map{ i => if(i%2==0) B(i) else C(i) }
val streamOfA = MessageStreamImpl(msg)
val streamOfB = streamOfA.filter[B]
val b: B = Await.result(streamOfB.next(), 1 second)
}
}
At compilation, I get warning: abstract type pattern B is unchecked since it is eliminated by erasure, and indeed the code does not work. If I execute Main, I get the error:
lang.ClassCastException: test.filterByType.Main$C cannot be cast to test.filterByType.Main$B
Which happens because it does not filter out the first list item C(1)
This small adjustment does the trick
import scala.reflect.ClassTag
def filter[B<:A](implicit C: ClassTag[B]): MessageStream[B] = new MessageStream[B]{
def next(): Future[B] = self.next.flatMap{
case b: B => Future.successful(b)
case _ => next()
}
}
I've found an alternative that works:
def collect[B<:A](f: PartialFunction[A,B]) = new MessageStream[B] {
override def next(): Future[B] = self.next().flatMap { m =>
if (f.isDefinedAt(m)) Future.successful(f(m))
else next()
}
}
More specifically, I have:
case class Key (key: String)
abstract class abstr {
type MethodMap = PartialFunction[Key, String => Unit]
def myMap: MethodMap // abstract
def useIt (key: Key, value: String) = {
val meth = myMap(key)
meth(value)
}
def report = {
for (key <- myMap.keySet) // how to do this
println("I support "+key)
}
}
I use it like this:
class concrete extends abstr {
var one: Boolean
def method1(v: String): Unit = ???
def method2(v: String): Unit = ???
def map1: MethodMap = {
case Key("AAA") => method1
}
def map2: MethodMap = {
case Key("AAA") => method2
}
override def myMap: MethodMap = if (one) map1 else map2
}
Of course, this is somewhat simplified, but the report function is necessary.
Some history: I first had it implemented using Map but then I changed it to PartialFunction in order to support the following override def myMap: MethodMap = if (one) map1 else map2.
Any suggestion to refactor my code to support everything is also appreciated.
No. PartialFunction can be defined (and often is) on infinite sets. E.g. what do you expect report to return in these situations:
class concrete2 extends abstr {
def myMap = { case Key(_) => ??? }
}
or
class concrete2 extends abstr {
def myMap = { case Key(key) if key.length > 3 => ??? }
}
? If you have a finite list of values you are interested in, you can do
abstract class abstr {
type MethodMap = PartialFunction[Key, String => Unit]
def myMap: MethodMap // abstract
val keys: Seq[Key] = ...
def report = {
for (key <- keys if myMap.isDefined(key))
println("I support "+key)
}
}
Some history: I first had it implemented using Map but then I changed it to PartialFunction in order to support the last line in second part.
Why? This would work just as well with Map.
In your solution, is there any way to define the domain of the partial function to be the finite set keys
def f: MethodMap = { case key if keys.contains(key) => ... }
Of course, the domain isn't part of the type.
Is it possible to perform a pattern match whose result conforms to a type parameter of the outer method? E.g. given:
trait Key[A] {
def id: Int
def unapply(k: Key[_]): Boolean = k.id == id // used for Fail2
def apply(thunk: => A): A = thunk // used for Fail3
}
trait Ev[A] {
def pull[A1 <: A](key: Key[A1]): Option[A1]
}
trait Test extends Ev[AnyRef] {
val key1 = new Key[String] { def id = 1 }
val key2 = new Key[Symbol] { def id = 2 }
}
Is there an implementation of Test (its pull method) which uses a pattern match on the key argument and returns Option[A1] for each key checked, without the use of asInstanceOf?
Some pathetic tries:
class Fails1 extends Test {
def pull[A1 <: AnyRef](key: Key[A1]): Option[A1] = key match {
case `key1` => Some("hallo")
case `key2` => Some('welt)
}
}
class Fails2 extends Test {
def pull[A1 <: AnyRef](key: Key[A1]): Option[A1] = key match {
case key1() => Some("hallo")
case key2() => Some('welt)
}
}
class Fails3 extends Test {
def pull[A1 <: AnyRef](key: Key[A1]): Option[A1] = key match {
case k # key1() => Some(k("hallo"))
case k # key2() => Some(k('welt))
}
}
None works, obviously... The only solution is to cast:
class Ugly extends Test {
def pull[A1 <: AnyRef](key: Key[A1]): Option[A1] = key match {
case `key1` => Some("hallo".asInstanceOf[A1])
case `key2` => Some('welt .asInstanceOf[A1])
}
}
val u = new Ugly
u.pull(u.key1)
u.pull(u.key2)
The problem is indeed that pattern matching ignores all erased types. However, there is a little implicit trickery that one could employ. The following will preserve the type resolution provided by the match for the return type.
abstract class UnErased[A]
implicit case object UnErasedString extends UnErased[String]
implicit case object UnErasedSymbol extends UnErased[Symbol]
class UnErasedTest extends Test {
def pull[ A1 <: AnyRef ]( key: Key[ A1 ])(implicit unErased: UnErased[A1]): Option[ A1 ] = unErased match {
case UnErasedString if key1.id == key.id => Some( "hallo" )
case UnErasedSymbol if key2.id == key.id => Some( 'welt )
case _ => None
}
}
val u = new UnErasedTest
println( u.pull( u.key1 ) )
println( u.pull( u.key2 ) )
This is however nearly equivalent to just defining separate sub classes of Key. I find the following method preferable however it may not work if existing code is using Key[String] that you can't change to the necessary KeyString (or too much work to change).
trait KeyString extends Key[String]
trait KeySymbol extends Key[Symbol]
trait Test extends Ev[ AnyRef ] {
val key1 = new KeyString { def id = 1 }
val key2 = new KeySymbol { def id = 2 }
}
class SubTest extends Test {
def pull[ A1 <: AnyRef ]( key: Key[ A1 ]): Option[ A1 ] = key match {
case k: KeyString if key1.id == k.id => Some( "hallo" )
case k: KeySymbol if key2.id == k.id => Some( 'welt )
case _ => None
}
}
val s = new SubTest
println( s.pull( s.key1 ) )
println( s.pull( s.key2 ) )
I provide here an extended example (that shows more of my context) based on the closed types approach of Neil Essy's answer:
trait KeyLike { def id: Int }
trait DispatchCompanion {
private var cnt = 0
sealed trait Value
sealed trait Key[V <: Value] extends KeyLike {
val id = cnt // automatic incremental ids
cnt += 1
}
}
trait Event[V] {
def apply(): Option[V] // simple imperative invocation for testing
}
class EventImpl[D <: DispatchCompanion, V <: D#Value](
disp: Dispatch[D], key: D#Key[V]) extends Event[V] {
def apply(): Option[V] = disp.pull(key)
}
trait Dispatch[D <: DispatchCompanion] {
// factory method for events
protected def event[V <: D#Value](key: D#Key[V]): Event[V] =
new EventImpl[D, V](this, key)
def pull[V <: D#Value](key: D#Key[V]): Option[V]
}
Then the following scenario compiles with not too much clutter:
object Test extends DispatchCompanion {
case class Renamed(before: String, now: String) extends Value
case class Moved (before: Int , now: Int ) extends Value
private case object renamedKey extends Key[Renamed]
private case object movedKey extends Key[Moved ]
}
class Test extends Dispatch[Test.type] {
import Test._
val renamed = event(renamedKey)
val moved = event(movedKey )
// some dummy propagation for testing
protected def pullRenamed: (String, String) = ("doesn't", "matter")
protected def pullMoved : (Int , Int ) = (3, 4)
def pull[V <: Value](key: Key[V]): Option[V] = key match {
case _: renamedKey.type => val p = pullRenamed; Some(Renamed(p._1, p._2))
case _: movedKey.type => val p = pullMoved; Some(Moved( p._1, p._2))
}
}
...and yields the desired results:
val t = new Test
t.renamed()
t.moved()
Now the only thing I don't get and I find ugly is that my cases must be of the form
case _: keyCaseObject.type =>
and cannot be
case keyCaseObject =>
which I would very much prefer. Any ideas where this limitation comes from?