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
Positive position: the type variable is the result/output/range/codomain of the function
Negative position: the type variable is the argument/input/domain of the function
When a type variable appears in positive position, the data type is covariant with that variable.
When the variable appears in negative position, the data type is contravariant with that variable.
flatMap
out, it should all fit
f
, Sum
is in positive position (return type)flatMap
is a function that takes f
, so all of f
is in negative position, which makes B
have positive position and Sum
have negative position
1
and -1
, they keep switching. Roughly speaking, functions of functions is the multiplication
List(1).withFilter(_ > 0)
val res63: scala.collection.WithFilter[Int,[_]List[_]] = scala.collection.IterableOps$WithFilter@cf76c0b
def something(list: List[_])
, then the [_]
is a wildcard, meaning you can pass a list value whose elements are any type.
[_]
there means "i don't care what the type is, so i'm going to forget its name", just like _
in places like pattern matching means "i don't care what this is, i'm not even going to name it")
if you use cats you can follow the examples at https://typelevel.org/cats/typeclasses/parallel.html#parallel
basically you write your validation functions to produce Either
. but you "glue together" those functions to act using Validated
, which then gets converted back to Either
at the end
mapN
from cats. it's like a mode: you switch from fail-fast (flatMap via Either) to collect-errors (mapN via Validated), and back.
val res63: scala.collection.WithFilter[Int, List] = ...
to be correct