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
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
Right, but now if you write flatMap
you f
is in turn in negative position, so all the variances switch
This (section of this) article explains it well but it's in Haskell