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.
Works
sealed trait Sum[+A, +B] {
def flatMap[AA >: A, C](f: B => Sum[AA, C]): Sum[AA, C] =
this match {
case Failure(v) => Failure(v)
case Success(v) => f(v)
}
}
final case class Failure[A](value: A) extends Sum[A, Nothing]
final case class Success[B](value: B) extends Sum[Nothing, B]
Doesn't Work
sealed trait Sum[+A, +B] {
def flatMap[C](f: B => Sum[A, C]): Sum[A, C] =
this match {
case Failure(v) => Failure(v)
case Success(v) => f(v)
}
}
final case class Failure[A](value: A) extends Sum[A, Nothing]
final case class Success[B](value: B) extends Sum[Nothing, B]
I don't get it. If I take f: B => Sum[A, C]
and construct it from trait Function1[-I, +O]{ def apply(i: I): O}
it seems B
is the contravariant argument while Sum[A, C]
is the covariant term in this function. How does it end up that A is the contravariant term also, what does this mean 👇 ?
"It is declared with type B => Sum[A, C] and thus a supertype is covariant in B and contravariant in A and C."
I expected it to be B is the contraviant term
, why? it's the argument to function f
,meaning it's B
that has to be type bounded not A and Sum[A, C] to be covariant
why? it's the result type of f
. But I'm clearly wrong, what I'm I missing here?
I didn't really get the argument for contravariant terms in a function why supertypes?
Source:
"Back to flatMap, the function f is a parameter, and thus in a contravariant position. This means we accept supertypes of f. It is declared with type B => Sum[A, C] and thus a supertype is covariant in B and contravariant in A and C. B is declared as covariant, so that is fine. C is invariant, so that is fine as well. A on the other hand is covariant but in a contravariant position. Thus we have to apply the same solution we did for Box above."
Excerpt From: Noel Welsh and Dave Gurnell. “Essential Scala”.
flatMap
takes f
that returns Sum