How to read complex scala type definition as in ```type Pipe[F[_], -I, +O] = Stream[F, I] => Stream[F, O] ```

Viewed 171

type Pipe[F[_], -I, +O] = Stream[F, I] => Stream[F, O]

I understand F[_], -I , +O. The missing part is F in the right. Why not Stream[F[_]]? I guess the F in the left and right have different meaning. Is there any official doc about the rule to write type definition?

I check the kind of Stream,

scala> :kind fs2.Stream
fs2.Stream's kind is X[+F[A1],+A2]

in another case:

scala> trait Functor [F[_]] {
     |    def map[A,B] (fn: A=>B)(fa: F[A]): F[B]
     | }
trait Functor

scala> :kind Functor
Functor's kind is X[F[A]]

why the F[A] is not replaced by F? Of course, I am confused with different concepts, but where should I learn them in one places.

1 Answers

I guess the F in the left and right have different meaning

More precisely, the underscore _ on the left and right have different meaning. Former represents anonymous type parameter, whilst latter represents a proper wildcard type. Future versions of Scala 3 should eventually make the meaning the same

It also removes the wart that, used as a type parameter, F[_] means F is a type constructor whereas used as a type, F[_] means it is a wildcard (i.e. existential) type. In the future, F[_] will mean the same thing, no matter where it is used

To illustrate the point consider

scala> trait Foo[F[_]]
// defined trait Foo

scala> type Bar[F[_]] = Foo[F[_]]
1 |type Bar[F[_]] = Foo[F[_]]
  |                     ^^^^
  |         Type argument F[?] does not have the same kind as its bound [_$1]

scala> type Bar[F[_]] = Foo[F[?]]
1 |type Bar[F[_]] = Foo[F[?]]
  |                     ^^^^
  |         Type argument F[?] does not have the same kind as its bound [_$1]

scala> type Bar[F[_]] = Foo[([x] =>> F[x])[?]]
1 |type Bar[F[_]] = Foo[([x] =>> F[x])[?]]
  |                     ^^^^^^^^^^^^^^^^^
  |       Type argument F[Any] does not have the same kind as its bound [_$1]

Note how underscore on the right is equivalent to

([x] =>> F[x])[?]

where type lambda [x] =>> F[x] is applied on proper type ?, which results in a proper type ([x] =>> F[x])[?]. But Foo is higher order type constructor which expects another type constructor as an argument, not a proper type. Hence the following works

scala> type Bar[F[_]] = Foo[F]
// defined alias type Bar[F] = Foo[F]

scala> type Bar[F[_]] = Foo[[x] =>> F[x]]
// defined alias type Bar[F] = Foo[F]

Note how type lambda [x] =>> F[x] has to be passed to Foo "unapplied".

Related