def fromLists(data: Map[String,String]): FailSlow[User]
Hi guys!
I am still playing with validations. A couple of questions. Is there a key difference between using Semigroupal.product and Semigroupal.tuple2. In the first case it is trait, in the 2nd it is object.
import cats.Semigroupal
import cats.data.Validated
import cats.data.Validated.Invalid
import cats.instances.list._ // for Monoid
type AllErrorsOr[A] = Validated[List[String], A]
def bothInvalid = {
Semigroupal[AllErrorsOr].product(
Validated.invalid(List("Error 1")),
Validated.invalid(List("Error 2"))
)
}
def bothInvalidTuple = {
Semigroupal.tuple2(
Validated.invalid(List("Error 1")),
Validated.invalid(List("Error 2"))
)
}
def bothValid = {
Semigroupal[AllErrorsOr].product(
Validated.valid(10),
Validated.valid(20)
)
}
def bothValidTuple = {
Semigroupal.tuple2(
Validated.valid(10),
Validated.valid(20)
)
}
With invalids both bothInvalid
and bothInvalidTuple
give the same result. With valid values, only the first one is compiled. The error I am getting:
Error:(40, 23) could not find implicit value for parameter semigroupal: cats.Semigroupal[[+A]cats.data.Validated[Nothing,A]]
Semigroupal.tuple2(
As I understand it now: In order to use Semigroupal.product
, I neen an instance of Validated
, which I get through:
import cats.data.Validated
import cats.data.Validated.Invalid
with Semigroupal.tupleN
I need in the scope to have an implicit instance of Validated??? But it seems I already have it with the previous imports, right? What do I miss?
Exactly this issue is described in the Scala with Cats book, p. 155:
Validated accumulates errors using a Semigroup , so we need one of those in scope to summon the Semigroupal . If no Semigroup is visible at the call site, we get an annoyingly unhelpful compila on error:
But I do not get it, where can I find Semigroup for positive Validated, if not in cats.data.Validated
@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)