Scala generic with trait matching - scala

Look at this code.
trait SomeMix {
}
trait Processor[T] {
def processMix(t: T with SomeMix) = {
println("processing T with Mix")
}
def processAsUsual(t:T)= {
println("processing T")
}
def process(t:T) = {
t match {
case mix: SomeMix => processMix(mix) // <---- error here
case _ => processAsUsual(t)
}
}
}
Stupid Scala compiler shows error here:
Error:(22, 39) type mismatch;
found : mix.type (with underlying type SomeMix)
required: T with SomeMix
case mix: SomeMix => processMix(mix)
It does not understand that expression I matching to SomeMix is already of type T. Ok lets help him. Changed code:
def process(t:T) = {
t match {
case mix: T with SomeMix => processMix(mix) // <---- warning here
case _ => processAsUsual(t)
}
}
Now it agrees that all is correct but show warning:
Warning:(22, 17) abstract type pattern T is unchecked since it is eliminated by erasure
case mix: T with SomeMix => processMix(mix)
Is any good way to avoid both error and warning here?

Scala compiler is not stupid. You can't check t is instance of T with SomeMix because of type erasure. Instead of dynamic type dispatching try to use typeclasses with static dispatching.
For example
trait SomeMix {
def someMethod: String = "test2"
}
class SomeType
def process[T](t: T)(implicit P: Process[T]): Unit = P.process(t)
trait Process[T] {
def process(t: T): Unit
}
implicit val processString: Process[SomeType] = s =>
println(s"processing $s as usual")
implicit val processStringWithSomeMix: Process[SomeType with SomeMix] = s =>
println(s"processing $s with mix ${s.someMethod}")
process(new SomeType)
process(new SomeType with SomeMix)

Since, as you mention, it's definitely an instance of T, you can just suppress the unchecked warning:
case mix: (T #unchecked) with SomeMix
Note that it's still unchecked and at runtime only tests that the matchee is an instance of SomeMix; if you change to e.g.
def process(t: Any) = ...
you'll get bad results.

Like this?
trait SomeMix {
}
trait Processor[T] {
def processMix(t: SomeMix) = {
println("processing SomeMix")
}
def processAsUsual(t:T)= {
println("processing T")
}
def process(t:T) = {
t match {
case mix: SomeMix => processMix(mix)
case _ => processAsUsual(t)
}
}
}

You can do this at compile time like #ppressives proposed.
If you really want to do this at runtime you should find a way to keep types there after compile time. In Scala standard way to do this is TypeTags.
Try
import reflect.runtime.universe.{TypeTag, typeOf}
def typ[A: TypeTag](a: A) = typeOf[A]
def process(t: T)(implicit ev: TypeTag[T with SomeMix], ev1: TypeTag[T]) = {
t match {
case mix if typ(t) <:< typeOf[T with SomeMix] => processMix(mix.asInstanceOf[T with SomeMix])
case _ => processAsUsual(t)
}
}
val p = new Processor[Int] {}
p.process(10) //processing T
val p1 = new Processor[Int with SomeMix] {}
val ten = 10.asInstanceOf[Int with SomeMix]
p1.process(ten) //processing T with Mix
Check
Pattern match of scala list with generics
Pattern matching on generic type in Scala

Related

Scala parsing generic argument of a function

I want to build a function that takes as input a generic function with one argument,
than parse the argument based on type and call the input function:
class PatternMatching {
val a = "test"
def test[T: TypeTag](callback: T => Unit): Unit = {
callback match {
case x if typeOf[T] <:< typeOf[String] => callback(a)
case x if typeOf[T] <:< typeOf[Array[Char]] => callback(a.toCharArray)
case _ => throw new IllegalArgumentException("error")
}
}
}
I see that type is correctly inferred but is not possible to invoke the function:
type mismatch;
found : PatternMatching.this.a.type (with underlying type String)
required: T
case x if typeOf[T] <:< typeOf[String] => callback(a)
I understand that it's expecting a type, but I can't find a way out.
You need explicit .asInstanceOf[T] with the method you've written:
callback(a.asInstanceOf[T])
Though, what you're trying to achieve is typically done with typeclass in Scala. I let the reader search about it but the general idea is you would have your method defined like this:
def test[T](callback: T => Unit)(implicit converter: ConverterFromString[T]): Unit = {
callback(converter.fromString(a))
}
And there would exist in scope some values of ConverterFromString for only some types you know how to handle.
The huge benefit of this approach is to be type-safe and if will raise errors at compile time rather than runtime if a type cannot be handled.
Using typeclass as suggested by #Gaƫl J works:
trait Converter[A] {
def conveterFromString(a: String): A
}
object Converter {
implicit val stringConverter: Converter[String] = new Converter[String] {
def conveterFromString(x: String): String = x
}
implicit val arrayConverter: Converter[Array[Char]] = new Converter[Array[Char]] {
def conveterFromString(x: String): Array[Char] = x.toCharArray
}
}
class PatternMatching {
val a = "test"
def test[A](callback: A => Unit)(implicit converter: Converter[A]): Unit =
callback(converter.conveterFromString(a))
}

Collection of object with self recursive type in scala

trait Node[N<:Node[N]] { self:N =>
}
trait A extends Node[A]
trait B {
def list:List[Node[_]]
def as:List[A] = list.collect { case x:A => x }
}
I'm having some trouble with using self recursive type in collections. In this example, the compiler gives the error
def as = list.collect { case x:A => x }
^
type arguments [_$1] do not conform to trait Node's type parameter bounds [N <: Node[N]]
because wildcard in List[Node[_]] does not conform the type bound. Is there any proper way of specifying wildcard type to recursive bound?
One workaround is
def as = {
list match {
case list:List[Node[n]] => list.collect { case x:A => x }
}
}
which is quite ugly.
you can solve this with an existential
trait Node[N<:Node[N]] { self:N =>
}
trait A extends Node[A]
trait B {
def list:List[X forSome {type X <: Node[X]}]
def as:List[A] = list.collect { case x:A => x }
}
just a fair warning forSome is going to be removed from scala at some point. Can you tell us more about what you're trying to do? mayber there's a more elegant way to do this
This appears to work:
trait B {
def list: List[_ <: Node[_]]
def as:List[A] = list.collect { case x:A => x }
}

Scala way to construct code of operations and types

I have the following operations: OpSUM, OpAVG, OpSTD, ... which I would like to implement for Int, Float, String and additional types.
The brute force way might be something like this:
trait Operation
case class OpSUM[T: TypeTag]() extends Operation {
typeOf[T] match {
case t if t =:= typeOf[String] => Do some string work
case t if t <:< typeOf[Int] => Do some Int work
...
}
}
case class OpAVG[T: TypeTag]() extends Operation {
typeOf[T] match {
case t if t =:= typeOf[String] => Do some string work
case t if t <:< typeOf[Int] => Do some Int work
...
}
}
...
Is there a better scala way to do this?
The main problem I see here is that Op and the type are coupled, meaning for each operation the type does different work.
If you try approach with type classes (as #mrmcgreg proposed in his comment) this can look like:
trait OpSUM[T] {
def doSomeWork
}
object OpSUM {
implicit val stringCase: OpSUM[String] = new OpSUM[String] {
override def doSomeWork = ???
}
implicit def intCase[T <: Int]: OpSUM[T] = new OpSUM[T] {
override def doSomeWork = ???
}
}
trait OpAVG[T] {
def doSomeOtherWork
}
object OpAVG {
implicit val stringCase: OpAVG[String] = new OpAVG[String] {
override def doSomeOtherWork = ???
}
implicit def intCase[T <: Int]: OpAVG[T] = new OpAVG[T] {
override def doSomeOtherWork = ???
}
}
// ...
I link also this explanation of Type Classes by Daniel Westheide since the application domain he uses for his post looks similar to yours.

Passing extractors dynamically for pattern matching

I want to be able to dynamically choose which extractors to use in my case class pattern matching.
I want something like:
def handleProcessingResult(extract: SomeType) : PartialFunction[Event, State] = {
case Event(someEvent: SomeEvent, extract(handlers)) =>
...
case Event(otherEvent: OtherEvent, extract(handlers)) =>
...
}
The idea is that I can have the above partial function, and can then use it anywhere where I know how to write an unapply to match and extract handlers from some pattern matched type.
If you are wondering why I want these partial functions, it is so that I can compose partial functions of common behaviour together to form the handlers for my states in an Akka FSM. This is not required to understand the question, but for example:
when(ProcessingState) {
handleProcessingResult(extractHandlersFromProcessing) orElse {
case Event(Created(path), Processing(handlers)) =>
...
}
}
when(SuspendedState) {
handleProcessingResult(extractHandlersFromSuspended) orElse {
case Event(Created(path), Suspended(waiting, Processing(handlers))) =>
...
}
It seems like this should be possible but I can't figure out how!
I have tried the following two simplifications:
object TestingUnapply {
sealed trait Thing
case class ThingA(a: String) extends Thing
case class ThingB(b: String, thingA: ThingA) extends Thing
val x = ThingA("hello")
val y = ThingB("goodbye", ThingA("maybe"))
process(x, new { def unapply(thing: ThingA) = ThingA.unapply(thing)})
process(y, new { def unapply(thing: ThingB) = ThingB.unapply(thing).map(_._2.a)})
def process(thing: Thing, extract: { def unapply[T <: Thing](thing: T): Option[String]}) = thing match {
case extract(a) => s"The value of a is: $a"
}
}
The idea being that I should be able to pass any sub-type of Thing and a suitable extractor to process. However, it doesn't compile due to:
[error] /tmp/proj1/TestUnapply.scala:10: type mismatch;
[error] found : AnyRef{def unapply(thing: TestingUnapply.ThingA): Option[String]}
[error] required: AnyRef{def unapply[T <: TestingUnapply.Thing](thing: T): Option[String]}
[error] process(x, new { def unapply(thing: ThingA) = ThingA.unapply(thing)})
[error] ^
[error] /tmp/proj1/TestUnapply.scala:11: type mismatch;
[error] found : AnyRef{def unapply(thing: TestingUnapply.ThingB): Option[String]}
[error] required: AnyRef{def unapply[T <: TestingUnapply.Thing](thing: T): Option[String]}
[error] process(y, new { def unapply(thing: ThingB) = ThingB.unapply(thing).map(_._2.a)})
[error] ^
Subsequently, moving the declaration of type parameter T onto process, gives us:
import scala.reflect.ClassTag
object TestingUnapply {
sealed trait Thing
case class ThingA(a: String) extends Thing
case class ThingB(b: String, thingA: ThingA) extends Thing
val x = ThingA("hello")
val y = ThingB("goodbye", ThingA("maybe"))
process(x, new { def unapply(thing: ThingA) = ThingA.unapply(thing)})
process(y, new { def unapply(thing: ThingB) = ThingB.unapply(thing).map(_._2.a)})
def process[T <: Thing: ClassTag](thing: Thing, extract: { def unapply(thing: T): Option[String]}) = thing match {
case extract(a) => s"The value of a is: $a"
}
}
Now gives us a different compilation error of:
[error] /tmp/TestUnapply.scala:18: Parameter type in structural refinement may not refer to an abstract type defined outside that refinement
[error] def process[T <: Thing: ClassTag](thing: Thing, extract: { def unapply(thing: T): Option[String]}) = thing match {
I am most likely doing something daft. Can someone help me please?
Try to make an workaround based on your first simplification, hope it helps.
object DynamicPattern extends App {
sealed trait Thing
case class ThingA(a: String) extends Thing
case class ThingB(b: String, thingA: ThingA) extends Thing
// change structural type to an abstract class
abstract class UniversalExtractor[T <: Thing] {
def unapply(thing: T): Option[String]
}
// extract is an instance of UniversalExtractor with unapply method
// naturally it's an extractor
def process[T <: Thing](thing: T, extract: UniversalExtractor[T]) =
thing match {
case extract(a) => s"The value of a is: $a"
}
val x = ThingA("hello")
val y = ThingB("goodbye", ThingA("maybe"))
val result1 = process(
x,
new UniversalExtractor[ThingA] {
def unapply(thing: ThingA) = ThingA.unapply(thing)
}
)
val result2 = process(y,
new UniversalExtractor[ThingB] {
def unapply(thing: ThingB) = ThingB.unapply(thing).map(_._2.a)
}
)
// result1 The value of a is: hello
println(" result1 " + result1)
// result2 The value of a is: maybe
println(" result2 " + result2)
}
Update
A probably "nasty" method without using an abstract class or trait hinted by the type conformance problem I explained later.
// when invoking process method, we actually know which subtype of
// Thing is pattern-matched, plus the type conformance problem,
// so here comes the ```unapply[T <: Thing](thing: T)```
// and ```asInstanceOf(which is usually not that appealing)```.
val thingAExtractor = new {
def unapply[T <: Thing](thing: T): Option[String] =
ThingA.unapply(thing.asInstanceOf[ThingA])
}
val thingBExtractor = new {
def unapply[T <: Thing](thing: T): Option[String] =
ThingB.unapply(thing.asInstanceOf[ThingB]).map(_._2.a)
}
// hello
println(process(x, thingAExtractor))
// maybe
println(process(y, thingBExtractor))
The reason it doesn't work in the two simplifications(actually the nasty method just pops up in my mind when I try to figure out the reason, so just write it here in case it helps).
For the first simplication: it's about the type conformance problem.
type ExtractType = { def unapply[T <: Thing](thing: T): Option[String] }
val anonExtractor = new { def unapply(thing: ThingA) = ThingA.unapply(thing) }
import scala.reflect.runtime.{ universe => ru }
import scala.reflect.runtime.universe.{ TypeTag, typeTag }
def getTypeTag[T: TypeTag](o: T) = typeTag[T].tpe
def getTypeTag[T: TypeTag] = ru.typeOf[T]
// false | so in turn type check fails at compilation time
println(getTypeTag(anonExtractor) <:< getTypeTag[ExtractType])
ScalaDoc Reflection's Runtime Classes in Java vs. Runtime Types in Scala part demonstrates the type conformance in similar case. In short, Scala compiler creates synthetic classes that are used at runtime in place of user-defined classes to be translated to equivalent Java Bytecode in cases mentioned in that part.
For the second simplication: this post parameter-type-in-structural-refinement-may-not-refer-to-an-abstract-type-define-outside has given some detailed explanation.

abstract type pattern is unchecked since it is eliminated by erasure

Could someone tell me how can I avoid the warning in the code block below:
abstract class Foo[T <: Bar]{
case class CaseClass[T <: Bar](t: T)
def method1 = {
case CaseClass(t: T) => println(t)
csse _ =>
}
}
This results in a compiler warning:
abstract type pattern T is unchecked since it is eliminated by erasure
case CaseClass(t: T) => println(t)
^
You could use ClassTag (or TypeTag):
import scala.reflect.ClassTag
abstract class Foo[T <: Bar : ClassTag]{
...
val clazz = implicitly[ClassTag[T]].runtimeClass
def method1 = {
case CaseClass(t) if clazz.isInstance(t) => println(t) // you could use `t.asInstanceOf[T]`
case _ =>
}
}
Another variation to use, especially if you desire to use a trait (as opposed to using a class or abstract class which the other solution requires), looks like this:
import scala.reflect.{ClassTag, classTag}
trait Foo[B <: Bar] {
implicit val classTagB: ClassTag[B] = classTag[B]
...
def operate(barDescendant: B) =
barDescendant match {
case b: Bar if classTagB.runtimeClass.isInstance(b) =>
... //do something with value b which will be of type B
}
}