Scala generics: why two types with the same upper bound are not compatible? - scala

I am confused with how bounds work in Scala generics. Consider the following:
sealed trait MessageBody
case class Body1() extends MessageBody
case class Body2() extends MessageBody
sealed trait MessageKey[T <: MessageBody]
case object Key1 extends MessageKey[Body1]
case object Key2 extends MessageKey[Body2]
type MessageHandler[B <: MessageBody] = PartialFunction[MessageKey[B], B => Unit]
abstract class Handler {
def messageHandler[B <: MessageBody]: MessageHandler[B]
}
class ProcessingHandler[ReturnType <: MessageBody](val onSuccess: MessageKey[ReturnType]) extends Handler {
override def messageHandler[B <: MessageBody]: MessageHandler[B] = {
case `onSuccess` => success
}
def success(msg: ReturnType): Unit = println(msg)
}
This gives me an error:
error: type mismatch;
found : ReturnType => Unit
required: B => Unit
case `onSuccess` => success
^
In my naive understanding, both ReturnType and B are subtypes of MessageBody and thus must be compatible. So why are they not compatible, and how should I write my code to make them compatible?
EDIT: The following piece of code works just fine, and if I mix the relation between the MessageKey and MessageBody instances anywhere, I get a nice compile time error.
class ProcessingHandler2 extends Handler {
override def messageHandler[B <: MessageBody]: MessageHandler[B] = {
case Key1 => h1
// case Key1 => h2 // gives compile-time error
case Key2 => h2
}
def h1(x: Body1): Unit = println(x)
def h2(x: Body2): Unit = println(x)
}
val handler: Handler = new ProcessingHandler2
handler.messageHandler(Key1)(Body1())
// handler.messageHandler(Key1)(Body2()) // gives compile-time error
EDIT: Obviously, there is no way to compile check this: handler.messageHandler(Key3) and this will result in a MatchError at run-time.

Let's say you have an object of type ProcessingHandler[Body1] and then call messageHandler[Body2] on it. According to the signature, it should return a Body2 => Unit, but you try to return a Body1 => Unit. If you were trying to return a MessageBody => Unit, that would be fine as MessageBody is a superclass of Body2, but Body1 is not. They're both children of the same superclass, but that doesn't make them compatible, that is you can't pass a Body2 as an argument to a function that takes a Body1 or vice versa.
Depending on what exactly your requirements are, there are multiple things you could do:
You could make Handler generic instead of messageHandler and then extend Handler[ReturnType] (or you could give Handler a member type and use it in messageHandler).
Or you could make success take a MessageBody as its argument instead of a ReturnType.

Related

How to chain generically chain functions returning Either with an operator like `andThen`?

Problem: Chaining multiple Either returning functions, the Left of which are all failures inheriting from a common sealed trait InternalError. However, the compiler complains that the chain is returning Either[_,Success] instead of Either[InternalError, Success].
Here's the code that does the chaining:
import scala.language.implicitConversions
object EitherExtension {
implicit class AndThenEither[A,B](val e: Function1[A,Either[_,B]]) {
//get ability to chain/compose functions that return aligning Eithers
def andThenE[C](f:Function1[B, Either[_,C]]): Function1[A, Either[_,C]] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
}
As was pointed out in the comments this discards the type of Left. If I change it the below it will not work since the final output can be of type Either[X|Y, C] which resolves to Either[_,C] and I'm back to square one.
implicit class AndThenEither[A,B,X](val e: (A) => Either[X, B]) {
def andThenE[C,Y](f:(B) => Either[Y, C]): (A) => Either[_, C] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
Here's the example showing the compositional failure of type alignment:
import EitherExtension._
object AndThenComposition {
//sample type definitions of failure responses
sealed trait InternalError
case class Failure1() extends InternalError
case class Failure2() extends InternalError
//sample type definitions
case class Id(id: Int)
case class Stuff()
//sample type definitions of successful responses
case class Output1()
case class Output2()
case class InputRequest()
val function1: (InputRequest) => Either[Failure1, Output1] = ???
val function2: (Output1) => Either[Failure2, Output2] = ???
def doSomething(s:Id, l:List[Stuff]): Either[InternalError, Output2] = {
val pipeline = function1 andThenE function2
pipeline(InputRequest()) //Why is this of type Either[_, Output2]
}
}
What am I missing? How can I get the return type to not be Either[Any, Output2] but rather the base/sealed trait? Is this possible to do generically?
You need to preserve the type of the left so we will modify the extension method to do that.
Note that, since both eithers can have different left types, what we will do is use a type bound to ask the compiler to infer the LUB between those types; thanks to Any this is always possibles (although not always helpful).
object EitherExtension {
implicit class AndThenEither[I, L1, R1](private val f: I => Either[L1, R1]) extends AnyVal {
def andThenE[L2 >: L1, R2](g: R1 => Either[L2, R2]): I => Either[L2, R2] =
i => f(i).flatMap(g)
}
}
Which can be used like this:
import EitherExtension._
object AndThenComposition {
sealed trait InternalError
final case object Failure1 extends InternalError
final case object Failure2 extends InternalError
val function1: Int => Either[Failure1.type, String] = ???
val function2: String => Either[Failure2.type, Boolean] = ???
def doSomething(input: Int): Either[InternalError, Boolean] = {
(function1 andThenE function2)(input)
}
}
See the code running here.
In case you're using this in production, and it's not just a learning thing, what you're looking for it's called Kleisli, and fortunately cats-core already implements it.
According to the cats-core docs:
Kleisli enables composition of functions that return a monadic value,
for instance an Option[Int] or a Either[String, List[Double]], without
having functions take an Option or Either as a parameter, which can be
strange and unwieldy.
Since Kleisli composes two functions with the signature A => F[B], you'd need only one abstraction to be able to use Kleisli, which is creating a new type for your operation:
type Operation[A] = Either[InternalFailure, A]
By doing this, you should be able to use Kleisli like this:
import cats.data.Kleisli
val first: Kleisli[Operation, InputRequest, Output1] = Kleisli { request: InputRequest =>
Left(Failure1())
}
val second: Kleisli[Operation, Output1, Output2] = Kleisli { output: Output1 =>
Right(Output2())
}
val composed = first.andThen(second)

Determining constructor parameter count and type via reflection in Scala

I have about a hundred small classes that inherit a trait. The classes are instantiated in a factory via reflection based on their names.
Class.forName(name).getConstructor().newInstance().asInstanceOf[Trait]
Now requirements have changed such that one and only one of the classes needs to take a parameter during construction. I am trying to change the factory to handle this new case. The REPL worksheet with my approach looks like this:
import java.lang.reflect.Constructor
trait T {
def p: Unit
}
class C1 extends T {
override def p = println("no ymd")
}
class C2(a: Array[String]) extends T {
override def p = println(s"year: ${a(0)}")
}
class C3(a: Array[Int]) extends T {
override def p = println(s"day: ${a(2)}")
}
val ymd = Array("2019","10","23")
val ymd_int = Array(2019,10,23)
def getT(c: Class[_]): T = {
c.getConstructors match {
case Array(c: Constructor[Array[String]]) => c.newInstance(ymd).asInstanceOf[T]
case Array(c: Constructor[Array[Int]]) => c.newInstance(ymd_int).asInstanceOf[T]
case Array(c: Constructor[_]) => c.newInstance().asInstanceOf[T]
case _ => throw new Exception("...")
}
}
getT(classOf[C1]).p
getT(classOf[C2]).p
getT(classOf[C3]).p
In the REPL, I'm using classOf instead of Class.forName because class names in the REPL are kind of wonky.
During compilation I'm getting the warnings:
Warning:(25, 23) non-variable type argument Array[String] in type
pattern java.lang.reflect.Constructor[Array[String]] is unchecked
since it is eliminated by erasure
case Array(c: Constructor[Array[String]]) =>
c.newInstance(ymd).asInstanceOf[T]
and
Warning:(26, 23) non-variable type argument Array[Int] in type
pattern java.lang.reflect.Constructor[Array[Int]] is unchecked
since it is eliminated by erasure
case Array(c: Constructor[Array[Int]]) =>
c.newInstance(ymd_int).asInstanceOf[T]
And then of course the calls to getT are failing because the three case statements look identical at run time and so all three calls are being handled by the first case.
Please Help.
Try
def getT(clazz: Class[_]): T = {
val constructor = clazz.getConstructors.head
(constructor.getParameterTypes.headOption.map(_.getSimpleName) match {
case None => constructor.newInstance()
case Some("String[]") => constructor.newInstance(ymd)
case Some("int[]") => constructor.newInstance(ymd_int)
case _ => throw new Exception("...")
}).asInstanceOf[T]
}

Passing function with Subclass as argument to function which takes any function with Super class as parameter

I have following scala code.
trait Super
case class Sub(value:String) extends Super
case class YetAnotherSub(value:String) extends Super
case class OnlyErrorType(value:String) extends Super
def function1[I <: Super, R](mapper : (I) => R, input: Super) (default: R): R = input match {
case error: OnlyErrorType =>
default
case success: I => mapper(success) // Ideally success => mapper(success)
case _ => default // I don't want this line at all, as I'm not expecting any other type
}
def function2(input:String):Super = if(input.size >= 3) Sub("Greater") else OnlyErrorType("Lesser")
def function3(input:String):String = {
val result = function2(input)
function1({sub:Sub => sub.value.toUpperCase}, result) ("Empty Result")
}
function3("Input")
There are various functions similar to function2 which accepts some parameters and return any subtype of Super. I would like to have a generic mapper, like function1, to map type Super to some other type, but return a default value in case of OnlyErrorType
In other words I would like to have some default handling for OnlyErrorType, but let calling function(in this case function3) specify the mapping for SuccessType (any sub types of Super except OnlyErrorType).
How do I achieve this ?
Above code compiles, but I hate to see warning,
warning: abstract type pattern I is unchecked since it is eliminated by erasure
I think there must be a better way to do this.
It's good you don't like the warning; in this case it basically means the test doesn't work.
The simplest approach is to make SuccessType explicit:
sealed trait Super
trait SuccessType extends Super
case class Sub(value:String) extends SuccessType
case class YetAnotherSub(value:String) extends SuccessType
case class OnlyErrorType(value:String) extends Super
def function1[R](mapper: SuccessType => R, input: Super) (default: R): R = input match {
case _: OnlyErrorType => default
case success: SuccessType => mapper(success)
}
Note that because Super is sealed, it can't be extended directly elsewhere, but one of its subtypes can be, so you can add new SuccessTypes. If that's not desired, make SuccessType sealed as well.
Of course, in this case function1({case sub:Sub => sub.value.toUpperCase}, result) ("Empty Result") will fail if it's passed a YetAnotherSub. If that's not what you want, then you need to distinguish "Super which is Sub if successful" and "Super which is YetAnotherSub if successful" statically. You can use
sealed trait Super[I <: Super[I]]
case class Sub(value:String) extends Super[Sub]
case class YetAnotherSub(value:String) extends Super[YetAnotherSub]
case class OnlyErrorType[I <: Super[I]](value:String) extends Super[I]
def function1[I <: Super[I], R](mapper : (I) => R, input: Super[I]) (default: R): R = input match {
case error: OnlyErrorType[_] =>
default
case success => mapper(success.asInstanceOf[I])
}
def function2(input:String):Super[Sub] = if(input.size >= 3) Sub("Greater") else OnlyErrorType("Lesser")
def function3(input:String):String = {
val result = function2(input)
function1({sub:Sub => sub.value.toUpperCase}, result) ("Empty Result")
}
function3("Input")
But I would prefer not to: the cast in function1 is actually safe, but it isn't trivially so (and defining new subtypes of Super incorrectly can break it).

Limitations of implicit resolution or type inference

I'm trying to understand why implicit resolution (or perhaps type inference) fails for the following Scala code. In this code, compilation fails on the second to last line, but succeeds on a modified version of the line where types are explicitly provided.
object O {
trait Wrapper[-A, +B] {
def func: A => B
}
object Identity
implicit class Identity2Wrapper[A](self: Identity.type) extends Wrapper[A, A] {
override def func: A => A = identity
}
// Compilation fails on the next line with error:
// found String("hello")
// required: A
Identity.func("hello")
// This line compiles.
implicitly[Identity.type => Wrapper[String, String]].apply(Identity).func("hello")
}
Travis Brown seems to be right, this is an occurence of the following: https://issues.scala-lang.org/browse/SI-6472
As a proof, I could make it compile using the work around given by Travis himself here: https://issues.scala-lang.org/browse/SI-6776
object O {
trait Wrapper[-A, +B] {
val funcFunc: A => B
def func( arg: A ): B = funcFunc( arg )
}
private class Private
trait BaseWrappable {
// Dummy method (cannot ever be called, just a work around to help the compiler)
def func( a: Private ) = ???
}
object Identity extends BaseWrappable
implicit class Identity2Wrapper[A](self: Identity.type) extends Wrapper[A, A] {
val funcFunc: A => A = identity
}
Identity.func("hello")
}
The code you have written is the same as:
class Identity2Wrapper[A](self: Identity.type) extends Wrapper[A, A] {
override def func: A => A = identity
}
implicit def identityToIdentityWrapper[A](self: Identity.type) = new Identity2Wrapper[A](self)
Note that the type parameter A is unbound until the the result from the call to func is used. The Scala compiler is not smart enough to look that far ahead and determine the type of A. Also, you cannot create a value of type [A] A => A in Scala. I'm actually surprised that the compiler doesn't infer A to be of type Nothing like it does when you call identityToIdentityWrapper explicitly.

How is this a type mismatch?

val eventListeners = new HashMap[Class[Event], ArrayBuffer[Event => Unit]]
def addEventListener[A <: Event](f: A => Unit)(implicit mf: ClassManifest[A]): A => Unit = {
eventListeners.getOrElseUpdate(mf.erasure.asInstanceOf[Class[Event]], ArrayBuffer[Event => Unit]()) += f
f
}
Is throwing:
error: type mismatch;
found : (A) => Unit
required: (this.Event) => Unit
eventListeners.getOrElseUpdate(mf.erasure.asInstanceOf[Class[Event]], ArrayBuffer[Event => Unit]()) += f
Why is it saying that it found (A) => Unit? The value of f is a function that is (Event) => Unit. Isn't A just a type parameter, not the signature?
Example call:
addEventListener { e:FooEvent => .... }
The class Function1 is contra-variant on its parameter. Ie, its type is Function1[-T, +R].
That means a function of Any => Unit is a subtype of Event => Unit, but for A a subtype of Event, A => Unit is a _super_type of Event => Unit.
Ergo, the problem. If you change the type parameter to A >: Event, it should work.
You are promising your ArrayBuffer that you will give it a function that can take any Event and turn it into a Unit (presumably doing something interesting along the way).
But you are giving it a function that can only take As, which may not encompass all Events. That is clearly not what you've promised, so the compiler complains.
You need to figure out what ought to happen in that case, and write code accordingly. For example, you could create a new function g that does nothing in case it receives an Event that, according to your class manifest, is not an A, and otherwise applies f. Or you could require all listeners to take events of all sorts, and be responsible themselves for throwing away the events that they don't want to bother with.
Edit: just to make things clearer with an example,
abstract class Fruit { def tasty: String }
class Banana extends Fruit { def tasty = "Yum!" }
abstract class SeededFruit extends Fruit {
def seedCount: Int
def tasty = "Mmm, mmm."
}
class Peach extends SeededFruit { def seedCount = 1 }
class Apple extends SeededFruit { def seedCount = 5 }
val tellAboutSeeds = (sf: SeededFruit) => println("There are "+sf.seedCount+"seeds")
val fruitTeller = new collection.mutable.ArrayBuffer[Fruit=>Unit]
fruitTeller += tellAboutSeeds // If this worked...
fruitTeller(0)(new Banana) // ...we'd be in trouble!