The type constructor F[_]
is required to be a member of the Functor
typeclass. This constraint is put on F
by the implicit parameter list
(implicit functor: Functor[F])
The whole signature
def doMath[F[_]](start: F[Int])(implicit functor: Functor[F]): F[Int]
might be interpreted as follows
Given any type constructor F
that is a member of Functor
typeclass, then doMath
can transform effectful value of type F[Int]
to
another effectful value of type F[Int]
.
where I use the phrase effectful value to emphasise that it is not a value of raw type such as Int
but instead a value of type constructed after applying type constructor F
to type argument Int
, namely F[Int]
.
Furthermore I use the phrase member of in the sense of
forms a, or participates in, or has a relationship
Note the usage of underscore _
in this context is not related to inference. The F[X]
and F[_]
type constructor notations mean exactly the same thing. The type argument X
is not used anywhere in the rest of the method signature, and for that reason by convention we use the underscore syntax F[_]
. Another convention is to use lower-case x
in F[x]
, as opposed to F[X]
, to emphasise x
is not used.
Indeed F[_]
is a type parameter in its own right and when type constructor Functor
is applied to it we get proper type Functor[F]
even though both F
and Functor
are type constructors, for example
scala> :kind -v List
List's kind is F[+A]
* -(+)-> *
This is a type constructor: a 1st-order-kinded type.
scala> :kind -v cats.Functor
cats.Functor's kind is X[F[A]]
(* -> *) -> *
This is a type constructor that takes type constructor(s): a higher-kinded type.
scala> :kind -v cats.Functor[List]
cats.Functor[List]'s kind is A
*
This is a proper type.