I am trying examples, described at https://github.com/underscoreio/scala-with-cats/blob/develop/src/pages/applicatives/validated.md
Cannot comple readUser function. Here is a list of all imports and readUser itself (all functions are implemented as described in that page):
import cats.Semigroupal
import cats.data.Validated
import cats.instances.string._ // for Semigroup
import cats.instances.vector._ // for Semigroupal
import cats.instances.list._ // for Semigroupal
import cats.syntax.apply._ // for mapN
import cats.syntax.validated._ // for valid and invalid
import cats.syntax.either._ // for catchOnly
case class User(name:String, age:Int)
def readUser(data: FormData): FailSlow[User] =
(
readName(data).toValidated,
readAge(data).toValidated
).mapN(User.apply)
The error I am getting, is related to the line, where mapN is invoked:
Error:(166, 7) value mapN is not a member of (cats.data.Validated[List[String],String], cats.data.Validated[List[String],Int])
possible cause: maybe a semicolon is missing before `value mapN'?
).mapN(User.apply)
Error:(166, 17) missing argument list for method apply in object User
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `apply _` or `apply(_,_)` instead of `apply`.
).mapN(User.apply)
what do I miss?
-Ypartial-unification
is enabled:)
Here is a simplified example, which can be copied as it is:
import cats.syntax.either._ // for ensure and for catchOnly
import cats.data.Validated
type FailFastList[A] = Either[List[String], A]
type FailSlow[A] = Validated[List[String], A]
def nonBlank(name: String)(data: String): FailFastList[String] =
Right(data).ensure(List(s"$name cannot be blank"))(_.nonEmpty)
case class User(name:String, nickname:String)
import cats.instances.list._ // for Semigroupal
import cats.syntax.either._ // for catchOnly
import cats.syntax.apply._ // for mapN
import cats.syntax.validated._ // for valid and invalid
def fromLists(data: Map[String,String]): FailSlow[String] =
(
nonBlank("name")(data.get("name").get).toValidated,
nonBlank("nickname")(data.get("nickname").get).toValidated
).mapN(User.apply)
Same error with mapN
One more question, related to fail slow. Here is an example, in which 2 dates are taken from map and validation checks if one dates comes after another. Most methods are copied from Cats with Scala, except the oneDateAfterAnother:
object DateFailSlow {
import cats.syntax.either._ // for ensure and for catchOnly
import java.text.{ParseException, SimpleDateFormat}
import java.util.Date
import cats.data.Validated
import cats.Semigroupal
type FailFast[A] = Either[List[String], A]
type FailSlow[A] = Validated[List[String], A]
def oneDateAfterAnotherFailFast(dateBefore: String, dateAfter: String)
(map: Map[String, String])(format: SimpleDateFormat)
: FailFast[Boolean]=
for {
dateBefore <- readDate(dateBefore)(map)(format)
dateAfter <- readDate(dateAfter)(map)(format)
} yield dateAfter.compareTo(dateBefore) > 0
def readDate(name:String)(map: Map[String, String])(format: SimpleDateFormat):FailFast[Date] =
for {
value <- readValue(name)(map)
res <- parseDate(name)(value)(format)
} yield res
def readValue(name: String)(map: Map[String, String]): FailFast[String] =
for {
value <- getValue(name)(map)
_ <- nonNullable(name)(value)
res <- nonBlank(name)(value)
} yield res
def getValue(name: String)(map: Map[String, String]): FailFast[String] =
map.get(name)
.toRight(List(s"$name field not specified"))
def nonNullable(name: String)(data: String): FailFast[String] =
Right(data).ensure(List(s"$name cannot be nullable"))(_ != null)
def nonBlank(name: String)(data: String): FailFast[String] =
Right(data).ensure(List(s"$name cannot be blank"))(_.nonEmpty)
def parseDate(name: String)(data: String)(format: SimpleDateFormat): FailFast[Date] =
Either.catchOnly[ParseException](format.parse(data)).
leftMap(_ => List(s"$name must be a date"))
}
It works fine, but I want to accumulate error s in oneDateAfterAnother. And it is an issue for me. Here is my try:
def oneDateAfterAnotherFailSlow(dateBefore: String, dateAfter: String)
(map: Map[String, String])(format: SimpleDateFormat)
: FailSlow[Boolean]=
Semigroupal.map2(
readDate(dateBefore)(map)(format).toValidated,
readDate(dateAfter)(map)(format).toValidated
)(_.compareTo(_) > 0)
As I understand map2 accepts 2 contexts, in my case it is to Validated and a function, that converts 2 arguments into the 3rd. Boolean in my case. But I am getting the error:
Error:(23, 4) type mismatch;
found : cats.data.Validated[List[String],java.util.Date]
required: ?F[?A0]
Note that implicit conversions are not applicable because they are ambiguous:
both method ArrowAssoc in object Predef of type [A](self: A)ArrowAssoc[A]
and method Ensuring in object Predef of type [A](self: A)Ensuring[A]
are possible conversion functions from cats.data.Validated[List[String],java.util.Date] to ?F[?A0]
)(_.compareTo(_) > 0)
Can you please explain, what I am doing wrong?
String
but User
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)