@noelwelsh , one more question, I am looking at example from the book:
case class User(name: String, age: Int)
def readName(data: FormData): FailFast[String]...
def readAge(data: FormData): FailFast[Int] ...
...
def readUser(data: FormData): FailSlow[User] =
(
readName(data).toValidated,
readAge(data).toValidated
).mapN(User.apply)
Let's assume I have another function for validation, that accepts name and age, validated by readName
and readAge
. For intstance:def validBoth(name:String, age:Int):FailFast[Tuple2[String,Int]]
How to compose these functions together? I cannot use mapN
, cause it require function, that does return raw value (not the context, in out case, not Validated). At least it did not work.
With fail fast it is quite simple, cause I use for-comrehension and have access to the results of readName
and readAge
:
for {
n <- readName...
i <- readAge...
t <- validBoth(n,i)
} yield t
but how to get the same result for failslow? Cause in this context it makes much more sense. validBoth - returns FailFast, but the whole composition should be failSlow
andThen
or similar on Validated
that does what you're looking for.
Parallel
construct.
@noelwelsh , here is a solution, I tried to apply to compose failfast with failslow:
import cats.syntax.either._
import cats.instances.list._ // for Semigroupal
def oneDateAfterAnotherFailSlow(dateBefore:String, dateAfter:String)
(map: Map[String, String])(format: SimpleDateFormat)
: FailFast[Tuple2[Date, Date]] =
for {
t <-Semigroupal[FailSlow].product(
readDate(dateBefore)(map)(format).toValidated,
readDate(dateAfter)(map)(format).toValidated
).toEither
r <- oneAfterAnother(t._1, t._2)
} yield r
Can you please comment it, how it looks, is it clear, is there a better approach?
Hi all,
A question with Readers.
Here is a simple service example, whose methods return reader:
trait Service1_1{
def s1f1:Reader[Map[String,Int],Int] =
Reader(_("name"))
def s1f2:Reader[Map[String,Int],Int] =
Reader(_("age"))
}
Here is a service-consumer, that accepts parameter, map, and also returns reader itself:
trait Service1_2 {
def s12f1(i:Int, map:Map[String,Int]):Reader[Service1_1, Int] =
Reader(s => {
val r = for {
r1 <- s.s1f1
r2 <- s.s1f2
} yield r1 + r2
r.run(map) + i
})
}
Ok, to use Service1_2.s12f1
I must have map in the parameter list:
object s1 extends Service1_1
object s2 extends Service1_2
val r = s2.s12f1(3, Map("age"-> 1, "name"-> 2)).run(s1)
The question: how to implement Service1_2.s12f2
:
trait Service1_2 {
def s2f2 = ???
}
In order to be able to run it like:
s2.s2f2(2)
.run(s1)
.run(Map("age"-> 1, "name"-> 2))
Cannot get it.
I could implement it:
def s2f2(i:Int): Reader[Service1_1, Reader[Map[String,Int],Int]] =
Reader(s => Reader(map => {
val r = for {
r1 <- s.s1f1
r2 <- s.s1f2
} yield r1 + r2
r.run(map) + i
}))
The question, is there a better approach? Or at least syntax? Cause with several levels of dependency, it will look strange.
Hi guys, theoretical question:
When we implement DI via Reader, we make a dependency a part of our method signature. Assume we have (without implementations):
trait Service1 { def f1:Int = ??? }
trait Service2 { def f2:Reader[Service1, Int] = ??? }
type Env= (Service1, Service2)
def c:Reader[Env, Int] = ??? //use Service2.f2 here
Now, f2
needs additional service for implementation, say:
trait Service3
type Service2Env = (Service1, Service3)
//new dependecies on both:
trait Service2 { def f2:Reader[Service2Env, Int] = ??? }
It will break existing clients, they cannot any longer to use Service2.f2
without providing Service3
additionally.
With DI via injection (via constructor or setters), which is common in OOP, I would use in a dependency only Service2
. How it is constructed and what is its list of dependencies, I do not care. From this point, any new dependencies in Service2
will keep the signature of c function unchanged.
How is it solved in FP? Are there options? Is there a way to inject new dependencies, but somehow protect customers from the change?
Hello there, I am reading the build.sbt
for Scala and looking at code like
publishArtifact in (Compile, packageDoc)
and
packageOptions in Compile in packageSrc
Do they both mean the same things as I read code? That is Compile:packageDoc::publishArtifact
and Compile:packageSrc::packageOptions
?
(1, "2", true)
it's really syntactic sugar for scala.Tuple3[Int, String, Boolean](1, "2", true)
Tuple1
, Tuple2
... and so on
case class Square(size: Double) extends Rectangular {
val height: Double = size
val width: Double = size
}
trait Rectangular extends Shape {
def height: Double
def width: Double
val sides: Int = 4
override val perimeter: Double = 2 * (height + width)
override val area: Double = height * width
}
trait Shape {
def sides: Int
def perimeter: Double
def area: Double
}
val square = Square(4)
println(square.height)
println(square.width)
println(square.size)
println(square.area)
println(square.perimeter)
println(square.sides)
4.0
4.0
4.0
0.0
0.0
4
perimeter
and area
should be defined with def
as methods rather than as vals
override def perimeter: Double = 2 * (height + width)
override def area: Double = height * width
Another way to do it is declare them lazy
. Then they will only be initialised upon first use
override lazy val perimeter: Double = 2 * (height + width)
override lazy val area: Double = height * width
Due to the immutability of the traits and classes, they will always be the correct value as neither height nor width will be subsequently updated.