Companion object in scala - scala

I am trying to write an toFirstOrSecond method that will allow me to call Try without any extra code.
object A:
enum FirstOrSecond[+V]:
case First(x: V) extends FirstOrSecond[V]
case Second(e : Throwable) extends FirstOrSecond[V]
def map[Q](f: V ⇒ Q): FirstOrSecond[Q] =
this match
case FirstOrSecond.Second(e) ⇒ FirstOrSecond.Second(e)
case FirstOrSecond.First(v) ⇒ Try { FirstOrSecond.First(f(v)) }.toFirstOrSecond match
case FirstOrSecond.First(m) => FirstOrSecond.First(f(v))
case FirstOrSecond.Second(m) => FirstOrSecond.Second(m)
object Try:
def toFirstOrSecond[Q](f: Try[Q]): FirstOrSecond[Q] =
f match
case Failure(e) => FirstOrSecond.First(e)
case Success(v) => FirstOrSecond.Second(v)
But I get an error object Try in object A does not take parameters How do i solve this? Thanks

Related

Add new method to type class in Scala

I'm trying to add new method toA() to utils.Try class.
But I am facing a problem of type:
case Success returns us v : Any, but I need v : V. How to get v : V?
Perhaps I need a function Apply? I'm new to this and I don't understand
object Example:
enum AorB[+V]:
case A(x: V) extends AorB[V]
case B(x : Throwable) extends AorB[V]
implicit class Try[V](x : V):
def apply[V](r: => V): Try[V] = ???
def toA[V](f: Try[V]): A[V] =
f match
case Success(v) => AorB.A(v)
case Failure(e) => AorB.B(e)

using Try in flatMap

Im writing my own class Success or Error. I want to create a way to build SuccessOrError from Try and then use Try instead of ugly try-catch in flatMap. But I'm stuck at this point. How should I best write a function fromTry in order to be able to write like this?
case SuccessOrError.Success(v) ⇒ SuccessOrError.fromTry(Try(f(v)))
enum SuccessOrError[+V]:
case Success(x: V) extends SuccessOrError[V]
case Error(e : Throwable) extends SuccessOrError[V]
def flatMap[Q](f: V ⇒ SuccessOrError[Q]): SuccessOrError[Q] =
this match
case SuccessOrError.Success(v) ⇒ try f(v) catch case NonFatal(e) => SuccessOrError.Error(e)
case SuccessOrError.Error(e) ⇒ SuccessOrError.Error(e)
object SuccessOrError:
def fromTry[Q](f: Try[Q]): SuccessOrError[Q] = ???
Converting from Try to another error model is quite a common practice. Perhaps consider how they approach it in scala-cats library as inspiration
Validated
/**
* Converts a `Try[A]` to a `Validated[Throwable, A]`.
*/
def fromTry[A](t: Try[A]): Validated[Throwable, A] =
t match {
case Failure(e) => invalid(e)
case Success(v) => valid(v)
}
or Either
/**
* Converts a `Try[A]` to a `Either[Throwable, A]`.
*/
def fromTry[A](t: Try[A]): Either[Throwable, A] =
t match {
case Failure(e) => left(e)
case Success(v) => right(v)
}
or ApplicativeError
/**
* If the error type is Throwable, we can convert from a scala.util.Try
*/
def fromTry[A](t: Try[A])(implicit ev: Throwable <:< E): F[A] =
t match {
case Success(a) => pure(a)
case Failure(e) => raiseError(e)
}
Note the pattern (bad pun, applogies) of simply pattern matching on happy and unhappy cases, so we can try emulating it
def fromTry[Q](f: Try[Q]): SuccessOrError[Q] =
f match {
case scala.util.Failure(e) => SuccessOrError.Error(e)
case scala.util.Success(v) => SuccessOrError.Success(v)
}

Writing typesafe code without boilerplate

I'm working on some system that uses external configuration and does some action depending upon the configuration provided. I have the following traits (methods omitted for simplicity):
sealed trait Tr[T]
case object Tr1 extends Tr[String]
case object Tr2 extends Tr[Int]
case object Tr3 extends Tr[Array[Byte]]
sealed trait Trr[T]
case object Trr1 extends Trr[String]
case object Trr2 extends Trr[Int]
case object Trr3 extends Trr[Array[Byte]]
trait Trrr[T]
case object Trrr1 extends Trrr[(String, Int)]
case object Trrr2 extends Trrr[(Int, String)]
case object Trrr3 extends Trrr[(Int, Int)]
case object Trrr4 extends Trrr[(String, String)]
case object Trrr5 extends Trrr[(String, Array[Byte])]
And the action:
def doUsefulAction[T, F](t1: Tr[T], t2: Trr[F], t3: Trrr[(T, F)]) = {
//...
}
The problem is the method invokation depends on the configuration:
def invokeWithConfig[T1, T2, T3](cfgTr1: String, cfgTr2: String, cfgTr3: String) = cfgTr1 match {
case "1" =>
cfgTr2 match {
case "1" =>
cfgTr3 match {
case "4" => doUsefulAction(Tr1, Trr1, Trrr4)
case _ => throw new IllegalArgumentException
}
case "2" =>
cfgTr3 match {
case "1" => doUsefulAction(Tr1, Trr2, Trrr1)
case _ => throw new IllegalArgumentException
}
case "3" =>
cfgTr3 match {
case "5" => doUsefulAction(Tr1, Trr3, Trrr5)
case _ => throw new IllegalArgumentException
}
case _ => throw new IllegalArgumentException
}
case "2" =>
//same boilerplate as above
case "3" =>
//same boilerplate as above
case _ => throw new IllegalArgumentException
}
The thing is there is tons of boilerplate with pattern matching. And this is just 3 traits. In case of 10 it becomes unreadable.
Is there a way to handle such a configuration yet staying types and avoid instanceOf?
Maybe macro can be useful here?
https://scalafiddle.io/sf/Z2NGo9y/0
This is a possible solution but it's not optimal yet you could eliminate more boilerplate by introducing something like shapeless/magnolia/scalaz-deriving to derive the implementations for the fromString implementations.
But really Validated and Applicative are your friends here
EDIT: As requested the code here
import cats._
import cats.implicits._
import cats.data.Validated._
import cats.data.ValidatedNel
import cats.data.NonEmptyList
sealed trait Tr[T]
case object Tr1 extends Tr[String]
case object Tr2 extends Tr[Int]
case object Tr3 extends Tr[Array[Byte]]
object Tr{
def fromString(s:String):ValidatedNel[Throwable, Tr[_]] = s match {
case "1" => Tr1.validNel
case "2" => Tr2.validNel
case "3" => Tr3.validNel
case _ => new RuntimeException(s"$s is not a valid Tr").invalidNel
}
}
sealed trait Trr[T]
case object Trr1 extends Trr[String]
case object Trr2 extends Trr[Int]
case object Trr3 extends Trr[Array[Byte]]
object Trr{
def fromString(s:String):ValidatedNel[Throwable, Trr[_]] = s match {
case "1" => Trr1.validNel
case "2" => Trr2.validNel
case "3" => Trr3.validNel
case _ => new RuntimeException(s"$s is not a valid Trr").invalidNel
}
}
trait Trrr[T, T1]
case object Trrr1 extends Trrr[String, Int]
case object Trrr2 extends Trrr[Int, String]
case object Trrr3 extends Trrr[Int, Int]
case object Trrr4 extends Trrr[String, String]
case object Trrr5 extends Trrr[String, Array[Byte]]
object Trrr{
def fromString(s:String):ValidatedNel[Throwable, Trrr[_, _]] = s match {
case "1" => Trrr1.validNel
case "2" => Trrr2.validNel
case "3" => Trrr3.validNel
case "4" => Trrr4.validNel
case "5" => Trrr5.validNel
case _ => new RuntimeException(s"$s is not a valid Trrr").invalidNel
}
}
def doUseful[T1, T2](tr:Tr[T1], trr:Trr[T2], trrr:Trrr[T1,T2]):String = "called"
def dispatch(s1:String, s2:String, s3:String):Either[Throwable, String] = (
Tr.fromString(s1),
Trr.fromString(s2),
Trrr.fromString(s3),
)
.tupled
.leftMap(
errs => new RuntimeException(
Foldable[NonEmptyList].intercalate(errs.map(_.getMessage),"\n")
)
)
.toEither
.flatMap {
case (a#Tr1, b#Trr2, c#Trrr1) => Right(doUseful(a,b,c))
case _ => Left(new RuntimeException("non mapped possibility"))
//note the line below won't compile because there's no valid combination of T1, T2 to call doUseful
//case (a#Tr1, b#Trr2, c#Trrr4) => doUseful(a,b,c)
}
println(dispatch("1", "2", "1"))
println(dispatch("1", "2", "15"))
println(dispatch("1", "20", "15"))

Monad transformer flatMap working

I´ve been playing monad transformer, and I just create one Future[Option]. But after read some blogs there´s something that is not explained and I don't understand.
Here in the implementation of my map and flatMap, in the flatMap once that I get the value of my option, and I apply the function over the value, I have to invoke over the function f(a) the .Value_passed_in_the_case_class(in this case future)
case class FutOpt[A](future: Future[Option[A]]) {
def map[B](f: A => B): FutOpt[B] = {
FutOpt(future.map(option => option.map(value => f(value)))
.recoverWith {
case e: Exception =>
Future.successful(Option.empty)
})
}
def flatMap[B](f: A => FutOpt[B]): FutOpt[B] =
FutOpt(future.flatMap(option => option match {
case Some(a) => f(a).future --> WHAT THIS .future IS DOING?
case None => Future.successful(None)
}))
}
What is happening there?, and how works?.
Regards.
What is happening there?
flatMap expects you to return it a FutOpt[B]:
def flatMap[B](f: A => FutOpt[B]): FutOpt[B]
The signature of your case class requires you to pass in a Future[Option[A]]:
case class FutOpt[A](future: Future[Option[A]])
This line of code:
FutOpt(future.flatMap(option => option match {
case Some(a) => f(a).future
Is constructing a new instance of FutOpt[B], and it needs a value of type Future[Option[B]] in order to be able to construct itself properly. f(a) returns a FutOpt[B], not a Future[Option[B]], and that is why it needs to access .future, which is of type Future[Option[B]].

Elegant Handling of Scala Future[Either]]

I have a type whose shape is like this:
val myType: Future[Either[MyError, TypeA]] = // some value
I know that I could pattern match on this and get to the Right or Left type, but the problem is that I would have to nest my pattern matching logic. I'm looking for much more elegant way of handling this? Any suggestions?
If you encode your MyError as an exception, you don't need the Either anymore and can simply patternMatch against the completion, or use a recoverWith to map it to another type:
myType.onComplete {
case Success(t) =>
case Failure(e) =>
}
To map your existing Either types you could do something like this:
case class MyException(e: MyError) extends Exception
def eitherToException[A](f: Future[Either[MyError,A]]): Future[A] = {
f.flatMap {
case Left(e) => Future.failed(MyException(e))
case Right(x) => Future.successful(x)
}
}
val myType2 = eitherToException(myType)
Alternatively, if MyError and TypeA are under your control, you could create a common super type and pattern match against that:
sealed trait MyResult
final case class MyError() extends MyResult
final case class TypeA() extends MyResult
myType.map {
case MyError() => ...
case TypeA() => ...
}
You can create custom extractor objects:
object FullSuccess {
def unapply[T](x: Try[Either[MyError, T]]) = x match {
case Success(Right(x)) => Some(x)
case _ => None
}
}
object PartSuccess {
def unapply[T](x: Try[Either[MyError, T]]) = x match {
case Success(Left(err)) => Some(err)
case _ => None
}
}
And
val myType: Future[Either[MyError, TypeA]] = // some value
myType.onComplete {
case FullSuccess(x) => ... // equivalent to case Success(Right(x))
case PartSuccess(x) => ... // equivalent to case Success(Left(x))
case Failure(e) => ...
}