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)
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]
}
I'm trying to go functional, but when working on real world problems I'm struggling, I need helps with a few basics. I like the idea of a type class and to add more implicit types in future.
trait Query {
def queryDetails: QueryDetails
}
case class LocalQueryType(queryDetails: QueryDetails) extends Query
case class JdbcQueryType(queryDetails: QueryDetails) extends Query
def queryTypeFactory(queryDetails: QueryDetails): Query = {
queryDetails.platform match {
case c if queryDetails.platform.contains("-file://") => LocalQueryType(queryDetails)
case _ => JdbcQueryType(queryDetails)
}
}
Then I have a type class that looks for the local or Jdbc types, but it doesn't work as it is receiving only Query type.
I've tried using generics like:
def queryTypeFactory[T<:Query](queryDetails: QueryDetails): T = {
queryDetails.platform match {
case c if queryDetails.platform.contains("-file://") => LocalQueryType(queryDetails)
case _ => JdbcQueryType(queryDetails)
}
}
Adding Type Class:
trait QueryTask[A] {
def runQuery(a: A): String
}
object QueryTask {
def apply[A](implicit sh: QueryTask[A]): QueryTask[A] = sh
object ops {
def runQuery[A: QueryTask](a: A) = QueryTask[A].runQuery(a)
implicit class ShowOps[A: QueryTask](a: A) {
def runQuery = QueryTask[A].runQuery(a)
}
}
implicit val localQuery: QueryTask[LocalQueryType] =
instance(localQueryType => s"running local: ${localQueryType.queryDetails.command} on platform: ${localQueryType.queryDetails.platform}")
implicit val jdbcQuery: QueryTask[JdbcQueryType] =
instance(jdbcQueryType => s"running jdbc: ${jdbcQueryType.queryDetails.command} on platform: ${jdbcQueryType.queryDetails.platform}")
def instance[A](func: A => String): QueryTask[A] =
new QueryTask[A] {
def runQuery(a: A): String = func(a)
}
The idea is to not use the usual OO factory or strategy pattern.
Type class approach seems not to work in your use case.
Implicits are resolved at compile time. So in order to decide which instance you need QueryTask[LocalQueryType] or QueryTask[JdbcQueryType] compiler has to know whether type A is LocalQueryType or JdbcQueryType at compile time.
But it seems you decide that depending on whether queryDetails.platform.contains("-file://") or not i.e. at runtime.
It seems you need usual pattern mathing. You should use type class pattern when it's necessary.
I don't get the following code to compile and I am curious of what I did wrong.
I defined a Contravariant Jsonwriter Trait and a function accepting implicit writers:
trait JsonWriter[-A] {
def write(value: A): Json
}
object Json {
def toJson[A](value: A)(implicit writer: JsonWriter[A]): Json =
writer.write(value)
}
Additionally I have defined some instances of these writers:
object JsonWriterInstances {
implicit val stringWriter: JsonWriter[String] =
(value: String) => JsString(value)
implicit val doubleWriter: JsonWriter[Double] =
(value: Double) => JsNumber(value)
class OptionWriter[-T](writer: JsonWriter[T]) extends JsonWriter[Option[T]] {
def write(value: Option[T]): Json = {
value match {
case None => JsNull
case Some(x) => writer.write(x)
}
}
}
implicit def optionWriter[T](implicit writer: JsonWriter[T]):
JsonWriter[Option[T]] = new OptionWriter[T](writer)
}
Now I have written a test:
"write double Option" in {
Some(1.0).toJson should be(JsNumber(1.0))
None.toJson should be(JsNull)
}
The first test for Some(1.0) works fine
The second one for None throws:
Error:(40, 12) could not find implicit value for parameter writer: JsonWriter[None.type]
None.toJson should be(JsNull)
If you want to try the code my JsonType definitions for this example are:
sealed trait Json
final case class JsObject(get: Map[String, Json]) extends Json
final case class JsString(get: String) extends Json
final case class JsNumber(get: Double) extends Json
case object JsNull extends Json
None, if you dont say anything else, is a Option[Nothing], so OptionWriter[Nothing] needs a JsonWriter[Nothing]. If you try Json.toJson(Some(1)) is the same, there is no JsonWriter[Int].
On the other hand, Json.toJson(None:Option[String]) works, because OptionWriter[String] can get a JsonWriter[String].
I think it has to do with the fact that
case object None extends Option[Nothing] { ... }
if you do one of the following, it will work
toJson(Option.empty[Double])
toJson(None : Option[Double])
Note that the second one uses type ascription to put a face, so to speak, on the Nothing (which is a subtype of everything)
I have a type hierarchy and want to ask a lookup method for an implementation. I am having trouble doing this without resorting to an asInstanceOf call.
So giving a simple type hierarchy like so
trait Vehicle
trait Flying extends Vehicle
class Plane extends Flying
trait Driving extends Vehicle
class Car extends Driving
trait Swimming extends Vehicle
class Boat extends Swimming
my lookup method is like this
def findVehicle[V <: Vehicle](implicit tag: TypeTag[V]): Option[V] = {
val v = tag.tpe match {
case t if t =:= typeOf[Flying] => Some(new Plane)
case t if t =:= typeOf[Driving] => Some(new Car)
case t if t =:= typeOf[Swimming] => Some(new Boat)
case _ => None
}
v.map(_.asInstanceOf[V])
}
with a lookup like so
println(findVehicle[Flying]) // Some(Plane#5b7fd935)
Is it possible to achieve a lookup like this without the asInstanceOf at the end?
You can avoid using TypeTag and use a type class instead.
trait Builder[+T] {
def build: Option[T]
}
implicit val flyingBuilder = new Builder[Flying] {
def build = Some(new Plane)
}
implicit val drivingBuilder = new Builder[Driving] {
def build = Some(new Car)
}
implicit val swimmingBuilder = new Builder[Swimming] {
def build = Some(new Boat)
}
implicit val anyBuilder = new Builder[Nothing] {
def build = None
}
def findVehicle[V <: Vehicle](implicit b: Builder[V]): Option[V] = b.build
No reflection involved and it's definitely more idiomatic.
Note how defining a Builder[Nothing] reproduces the same behavior you achieved by returning a None. This is not necessarily a good idea, as you're now forced to check whether the method was able to produce a value or not.
I would rather prefer a compile-time error if it's impossible to build the instance of the desired type, and you achieve it by directly returning T as opposed to Option[T] from build (and of course getting rid of the Nothing case).