Difference between F[_] and F[T] In Scala when used in type constructors

↘锁芯ラ 提交于 2020-08-22 09:39:06

问题


This question is about _ as used in type constructor and not when used in defining existential types.

So the question is what is the difference when _ is used as type parameter instead of a variable like T. For example difference between F[_] and F[T].

The only difference I can think of is that with F[_] the parameter itself can have as many holes as possible...that is F[_] can become F[Int] or F[Future[Option[Int]]] etc...while when you have F[T] the T can only be a proper type...that is F[String] or F[Int] etc.

Is this a correct assumption? and is that the main difference between F[_] and F[T]? or there are more?

What about the case where the two are used as type parameters? For example, what is the difference between trait Functor [F[_]] and trait Functor [F[T]]?

Is there any semantic difference if the functor trait is defined as trait Functor [F[_]] instead of trait Functor [F[T]]?


回答1:


To quote the specification:

The above scoping restrictions are generalized to the case of nested type parameter clauses, which declare higher-order type parameters. Higher-order type parameters (the type parameters of a type parameter t) are only visible in their immediately surrounding parameter clause (possibly including clauses at a deeper nesting level) and in the bounds of t. Therefore, their names must only be pairwise different from the names of other visible parameters. Since the names of higher-order type parameters are thus often irrelevant, they may be denoted with a _, which is nowhere visible.

Example

Here are some well-formed type parameter clauses:

[S, T]
[@specialized T, U]
[Ex <: Throwable]
[A <: Comparable[B], B <: A]
[A, B >: A, C >: A <: B]
[M[X], N[X]]
[M[_], N[_]] // equivalent to previous clause
[M[X <: Bound[X]], Bound[_]]
[M[+X] <: Iterable[X]]

So if you have no bounds, as in Functor [F[T]], there's no difference at all from Functor [F[_]].




回答2:


The only difference I can think of is that with F[_] the parameter itself can have as many holes as possible...that is F[_] can become F[Int] or F[Future[Option[Int]]] etc...while when you have F[T] the T can only be a proper type...that is F[String] or F[Int] etc.

Is this a correct assumption?

Note that Future[Option[Int]] is a proper type of the same kind as Int or String. We can convince ourself by using :kind command in Scala REPL

scala> :kind -v Future[Option[Int]]
scala.concurrent.Future[Option[Int]]'s kind is A
*
This is a proper type.

scala> :kind -v Int
Int's kind is A
*
This is a proper type.

To drive the point, consider the following complicated-looking type

Function3[Int, Tuple2[Double, List[Int]], Char, Future[Either[String, Int]]]

It is still just a simple concrete * type

scala> :kind -v Function3[Int, Tuple2[Double, List[Int]], Char, Future[Either[String, Int]]]
(Int, (Double, List[Int]), Char) => scala.concurrent.Future[Either[String,Int]]'s kind is A
*
This is a proper type.

Hence we see the shape of Future[Option[Int]] is simply *, and F[_] does not need any extra "holes" to fit it. Both F[_] and F[T] type constructors take a type argument of exactly same shape, namely *, no more, nor less. For example, let us try to fit in more than it can handle

trait Bar[F[_]]   // Bar is type constructor of higher order kind  - shape (* -> *) -> *
def g[F[_]] = println("g takes type constructor type argument of * -> * shape")

scala> g[Bar]
        ^
       error: kinds of the type arguments (Bar) do not conform to the expected kinds of the type parameters (type F).
       Bar's type parameters do not match type F's expected parameters:
       type F has 1 type parameter, but type _ has 0

This errors because actual (* -> *) -> * shape of Bar does not fit expected * -> * shape of F.



来源:https://stackoverflow.com/questions/49762658/difference-between-f-and-ft-in-scala-when-used-in-type-constructors

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!