I'm new to cats. I'm creating State instances to handle deserialisation of types from a byte stream. e.g.
val int: State[Seq[Byte], Int] = State[Seq[Byte], Int] {
case bs if bs.length >= 4 =>
bs.drop(4) -> ByteBuffer.wrap(bs.take(4).toArray).getInt
case _ => throw new EOFException()
}
I have implemented a parser of Option[Int] in terms of the above, like so:
val unit: State[Seq[Byte], Unit] = State[Seq[Byte], Unit](_ -> Unit)
val optInt: State[Seq[Byte], Option[Int]] = int.flatMap(i =>
if (i == 1) int.map(Some(_)) else unit.map(_ => None)
)
I feel that I've missed a trick here, as the implementation seems too verbose. Can I write this more succinctly? Can I do away with needing to define unit?
I wouldn't say that's too verbose, but I'd do two tricks with this:
Replace conditional with pattern matching function
Use State.pure instead of manually creating/transforming State values such as your unit.
val optInt: State[Seq[Byte], Option[Int]] = int.flatMap {
case 1 => int.map(Some(_))
case _ => State.pure(None)
}
Related
I have many functions in my code defined with return type as Either[Throwable, String] and all of them have one argument of type String. Three representative functions of my code are defined as:
val f1 = (input: String) => {
/* Processing from the input using a library in my actual code returns a Either[Throwable, String] */
if (input == "a") Left(new Exception(input))
else Right("Success")
}
val f2 = (input: String) => {
if (input == "b") Left(new Exception(input))
else Right("Success")
}
val f3 = (input: String) => {
if (input == "c") Left(new Exception(input))
else Right("Success")
}
To chain the function outputs, I'm writing code like:
def x(input: String) = f1(input) match {
case Left(value) => Left(value)
case Right(_) => f2(input) match {
case Left(value) => Left(value)
case Right(_) => f3(input)
}
}
Since this is just three functions so this might look like a short code. However there are multiple such matches that are happening in my code, so it's a very long code. I am looking to avoid such a chaining.
I know that Scala has a way to chain functions like f1.andThen(f2).andThen(f3), however the problem is that in each andThen we need to pass the same argument, in this case being input. However I want to chain these functions so that if there is a Left output, it should not go to the next andThen.
I believe this can be simplified using Functional Programming, but I don't know where to start. Is there a way we can achieve this using Scala functional programming?
If you have cats in scope, then all you need to do is this:
import cats.syntax.all._
val functions: List[String => Either[Throwable, Unit]] = List(
// put your functions here.
)
val result: Either[Throwable, Unit] =
functions.traverse_(f => f(input))
Otherwise, you may emulate it using this:
val init: Either[Throwable, Unit] = Right(())
functions.foldLeft(init) {
case (acc, f) =>
acc.flatMap(_ => f(input))
}
This code works fine so far but i don't like the tuples everywhere like this which leads to using _.1 and _2 etc which is less expressive.
I can implement wrapper classes that which have more expressive names.
Is there a better approach?
trait DrawingSteps {
def prompt(savedCommands: List[Command]): IO[(List[Command], Unit)]
def read(savedCommands: List[Command]): IO[(List[Command], String)]
def parseAndAppend(in: (List[Command], String)): List[Command]
def invoke(savedCommands: List[Command]): IO[(List[Command], Unit)]
def drawingProgram(savedCommands:List[Command] = List()):IO[(List[Command],Unit)] = for {
t <- prompt(savedCommands)
rawCommand <- read(t._1)
commands = parseAndAppend(rawCommand)
output <- invoke(commands)
} yield output._1 match {
case (_:CommandIsQuit)::_ => FP.exit(output._1).run
case _ => drawingProgram(output._1).run
}
}
You can always use pattern matching to deconstruct case classes/tuples and give meaningful names instead of using tuple elements (_1). Here is an example:
trait DrawingSteps {
def prompt(savedCommands: List[Command]): IO[(List[Command], Unit)]
def read(savedCommands: List[Command]): IO[(List[Command], String)]
def parseAndAppend(in: (List[Command], String)): List[Command]
def invoke(savedCommands: List[Command]): IO[(List[Command], Unit)]
def drawingProgram(savedCommands:List[Command] = List()):IO[(List[Command],Unit)] = for {
(inCmd, _) <- prompt(savedCommands)
rawCommand <- read(inCmd)
commands = parseAndAppend(rawCommand)
(outCmd, _) <- invoke(commands)
} yield outCmd match {
case (_:CommandIsQuit)::_ => FP.exit(outCmd).run
case _ => drawingProgram(outCmd).run
}
}
I guess you could replace tuples with HLists if you find yourself having lots of tuples with arity greater than 2. Looking at your code you have lots of repetitive return types like IO[(List[Command], Unit)] which can be declared with type alias or could benefit from being refactored into a case class for even better naming.
I would like to add an element depending on the result of a different condition.
As it is now, I did it this way :
val add1 = if(condition1) Seq(ENUM_ELEMENT_1) else Seq()
val add2 = if(condition2) Seq(ENUM_ELEMENT_2) else Seq()
return Seq(add1, add2).flatten
If I was in Java I would just create an empty ArrayList() at the beginning and add to this list as the code encounter the ifs.
But in Scala, I would have to use a mutable object of Seq and I don't know if it's appropriate here.
Declare list of tuples with conditions on left and enums on right:
val conditions: Seq[(Boolean, Enum)] = List(
condition1 -> ENUM_ELEMENT1,
condition2 -> ENUM_ELEMENT2
)
Then you can just reduce it with collect:
val result: Seq[String] = conditions.collect{
case (true, v) => v
}
or flatMap:
val result: Seq[Enum] = conditions.flatMap{
case (true, v) => Some(v)
case _ => None
}
There is several ways to do this. Here's what come out of the blue to me:
(if(condition1) Seq(ENUM_ELEMENT_1) else Seq()) ++ (if(condition2) Seq(ENUM_ELEMENT_2) else Seq())
They are way of factorizing both of this procedure by a function or a fold but it may be overthinking at this state.
Without proper context I am unable to provide a more concrete solution, but I think this pseudo-code represents your problem.
If you have any questions, do not doubt to ask for clarification.
object ListFactory {
type Input = ???
type Output = ???
private val allElements: List[Output] =
List(ENUM_ELEMENT_1, ENUM_ELEMENT_2, ???)
private val conditions: List[Input => Boolean] =
List(???)
def apply(input: Input): List[Output] =
(allElements zip conditions).collect {
case (element, condition) if (condition(input)) => element
}
}
ListFactory(???) // res1: List[Output] = ???
I would like to achieve something akin to the strategy pattern in scala without resorting to pattern matching with a long list of case statements. Here is roughly what I have in mind:
trait HandlerTrait {
def handlerA(...): Unit
def handlerB(...): Unit
}
SomeClass1 extends HandlerTrait {
override def handlerA(...) {...}
override def handlerB(...) {...}
}
SomeClass2 extends HandlerTrait {
override def handlerA(...) {...}
override def handlerB(...) {...}
}
object MyApp extends App {
// 1. define bindings for these implementations
val myBindings = Map(x -> someClass1, y -> someClass2)
// 2. Such that implementation of someMethod targeting handlerA implementations could look like this:
def someMethod(object: ObjectType): Unit = {
myBindings.get(object.x) match {
case Some(entry) => entry.handlerA(object)
case None => ()
}
}
}
A few more things:
I don't know how many SomeClassXXX I will have. I will add as needed
to provide customizations on how to handle A/B/C ...
Given a key, I want to dispatch to the correct class and execute the targeted handler.
Is there a better more concise way of achieving this in scala?
I think one way to reduce boilerplate is to use type system rather than inheritance. For instance, if your handler is of type T => Unit then any function that satisfies this type can be a handler, there is no need to officially declare HandlerTrait and even someMethod.
Whether to use a Map or cases to map from a key to a handler is up to you. Both can be extended to handle new cases.
Here is an example to sum up what I'm proposing:
val currentlyDefinedStrategies: PartialFunction[String, Unit] = {
case "1" => println(1)
case "2" => println(2)
}
val newStrategies: PartialFunction[String, Unit] = {
case "3" => println(3)
}
val defaultStrategy: PartialFunction[String, Unit] = {
case _ => println("default")
}
And usage:
scala> currentlyDefinedStrategies("1")
1
scala> currentlyDefinedStrategies("3")
scala.MatchError: 3 (of class java.lang.String) ...
scala> currentlyDefinedStrategies.orElse(newStrategies)("3")
3
scala> currentlyDefinedStrategies.orElse(newStrategies)("4")
scala.MatchError: 4 (of class java.lang.String)
scala> currentlyDefinedStrategies.orElse(newStrategies).orElse(defaultStrategy)("4")
default
You can achieve similar pattern with Map or using other FP techniques. The main point is to keep the most relevant code and get rid of boilerplate. Of course, HandlerTrait might be useful to you for structuring your code and thinking in terms of classes rather than functions, but the idea is the same.
See also: https://pavelfatin.com/design-patterns-in-scala/#strategy
The above example is a bit simplified and you actually want to pass parameters to handler (println in our case). Here is how:
val currentlyDefinedStrategies: Int => PartialFunction[String, Unit] = (x) => {
case "1" => println("1: " + x)
case "2" => println("2: " + x)
case _ => println("default: " + x)
}
You can fix the argument without choosing a strategy:
scala> val noStrategy = currentlyDefinedStrategies(1)
noStrategy: PartialFunction[String,Unit] = <function1>
... and provide strategy afterwards:
scala> noStrategy("1")
1: 1
Or apply the strategy right away:
scala> currentlyDefinedStrategies(1)("1")
1: 1
You can also decide on your strategy first and then pass an argument:
val currentlyDefinedStrategies: PartialFunction[String, Int => Unit] = {
case "1" => x => println("1: " + x)
case "2" => x => println("2: " + x)
case _ => x => println("default: " + x)
}
scala> val handlerWithChosenStrategy = currentlyDefinedStrategies("1")
handlerWithChosenStrategy: Int => Unit = $anonfun$1$$Lambda$1374/666224848#59a9f3eb
scala> handlerWithChosenStrategy(1)
1: 1
I think the point is that FP is so rich and flexible that strategy pattern is really not a thing. It's basically just some function type that suits your convenience like type Strategy[T, -A, +B] = PartialFunction[T, A => B]. Example:
scala> type Strategy[T, -A, +B] = PartialFunction[T, A => B]
defined type alias Strategy
val currentlyDefinedStrategies: Strategy[String, Int, Unit] = {
case "1" => x => println("1: " + x)
case "2" => x => println("2: " + x)
case _ => x => println("default: " + x)
}
currentlyDefinedStrategies: Strategy[String,Int,Unit] = <function1>
scala> currentlyDefinedStrategies("1")(1)
1: 1
A more advanced concept would be an Expression Problem (here) where you need to extend both the operations you can do on types as well as adding new types.
I want to update a sequence in Scala, I have this code :
def update(userId: Long): Either[String, Int] = {
Logins.findByUserId(userId) map {
logins: Login => update(login.id,
Seq(NamedParameter("random_date", "prefix-" + logins.randomDate)))
} match {
case sequence : Seq(Nil, Int) => sequence.foldLeft(Right(_) + Right(_))
case _ => Left("error.logins.update")
}
}
Where findByUserId returns a Seq[Logins] and update returns Either[String, Int] where Int is the number of updated rows,
and String would be the description of the error.
What I want to achieve is to return an String if while updating the list an error happenes or an Int with the total number of updated rows.
The code is not working, I think I should do something different in the match, I don't know how I can check if every element in the Seq of Eithers is a Right value.
If you are open to using Scalaz or Cats you can use traverse. An example using Scalaz :
import scalaz.std.either._
import scalaz.std.list._
import scalaz.syntax.traverse._
val logins = Seq(1, 2, 3)
val updateRight: Int => Either[String, Int] = Right(_)
val updateLeft: Int => Either[String, Int] = _ => Left("kaboom")
logins.toList.traverseU(updateLeft).map(_.sum) // Left(kaboom)
logins.toList.traverseU(updateRight).map(_.sum) // Right(6)
Traversing over the logins gives us a Either[String, List[Int]], if we get the sum of the List we get the wanted Either[String, Int].
We use toList because there is no Traverse instance for Seq.
traverse is a combination of map and sequence.
We use traverseU instead of traverse because it infers some of the types for us (otherwise we should have introduced a type alias or a type lambda).
Because we imported scalaz.std.either._ we can use map directly without using a right projection (.right.map).
You shouldn't really use a fold if you want to exit early. A better solution would be to recursively iterate over the list, updating and counting successes, then return the error when you encounter one.
Here's a little example function that shows the technique. You would probably want to modify this to do the update on each login instead of just counting.
val noErrors = List[Either[String,Int]](Right(10), Right(12))
val hasError = List[Either[String,Int]](Right(10), Left("oops"), Right(12))
def checkList(l: List[Either[String,Int]], goodCount: Int): Either[String, Int] = {
l match {
case Left(err) :: xs =>
Left(err)
case Right(_) :: xs =>
checkList(xs, (goodCount + 1))
case Nil =>
Right(goodCount)
}
}
val r1 = checkList(noErrors, 0)
val r2 = checkList(hasError, 0)
// r1: Either[String,Int] = Right(2)
// r2: Either[String,Int] = Left(oops)
You want to stop as soon as an update fails, don't you?
That means that you want to be doing your matching inside the map, not outside. Try is actually a more suitable construct for this purpose, than Either. Something like this, perhaps:
def update(userId: Long): Either[String, Int] = Try {
Logins.findByUserId(userId) map { login =>
update(login.id, whatever) match {
case Right(x) => x
case Left(s) => throw new Exception(s)
}
}.sum
}
.map { n => Right(n) }
.recover { case ex => Left(ex.getMessage) }
BTW, a not-too-widely-known fact about scala is that putting a return statement inside a lambda, actually returns from the enclosing method. So, another, somewhat shorter way to write this would be like this:
def update(userId: Long): Either[String, Int] =
Logins.findByUserId(userId).foldLeft(Right(0)) { (sum,login) =>
update(login.id, whatever) match {
case Right(x) => Right(sum.right + x)
case error#Left(s) => return error
}
}
Also, why in the world does findUserById return a sequence???