I am relatively new to the Scala language, and I came across code that looks like this (while reviewing Akka).
val someFlow: Flow[Int, Int, NotUsed] = Flow[Int].reduce[Int](_ + _)
How can I read type annotations like: Flow[Int, Int, NotUsed]?
I am relatively new to the Scala language, and I came across code that looks like this (while reviewing Akka).
val someFlow: Flow[Int, Int, NotUsed] = Flow[Int].reduce[Int](_ + _)
How can I read type annotations like: Flow[Int, Int, NotUsed]?
 
    
    Consider a value-level function application
((x: Int) => List(x))(42)
where value-lambda (x: Int) => List(x) is applied to value argument 42.
Consider a type-level function application
([X] =>> List[X])[Int]
where type-lambda [X] =>> List[X] is applied to type argument Int.
Now compare the two in
scala> val v: ([X] =>> List[X])[Int] = ((x: Int) => List(x))(42)
val v: List[Int] = List(42)
We see that a type lambda or type constructor constructs a proper type List[Int] which accommodates a value List(42) constructed by a value lambda or value constructor.
Note the similarity between => and =>>. Now =>> syntax is only available in Dotty (Scala 3) which brings type lambdas, however I believe it is already a helpful device to grok what types such as Flow[Int, Int, NotUsed] really are
([In, Out, Mat] =>> Flow[In, Out, Mat])[Int, Int, NotUsed]
These are the facilities to support the techniques of polymorphism which allows us to think in ever higher levels of abstraction. For example, if f takes a box of chocolates
def f(v: Box[Chocolate] = ???
then g takes a box of something A
def g[A](v: Box[A]) = ???
and we can go even higher where h takes some kind of container F of something A
def h[F[_], A](v: F[A]) = ???
This last form of polymorphism is known as type constructor polymorphism and is what separates Scala from many other languages which stop at the box of something.
