4
votes

I'm reading about higher kinded types in Scala and I can't wrap my head around the use of the underscore.

For example:

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

val listFunctor = new Functor[List] {
  override def map[A, B](fa: List[A])(f: A => B) = fa.map(f)
}

listFunctor.map(List(1,2,3))(_ + 3)

If I use a parametrised type A in the trait, I get the same result:

trait Functor2[F[A]] {
  def map[A, B](fa: F[A])(f: A => B) : F[B]
}

val listFunctor2 = new Functor2[List] {
  override def map[A, B](fa: List[A])(f: A => B) = fa.map(f)
}

listFunctor2.map(List(1,2,3))(_ + 3)

Is there a significant difference between the two examples?

1
In your second example your A comes from map[A, B]. The one defined in F[A] is a different one. It's just a placeholder. And it can't be used outside of the signature. In general, a higher-kinded type is just a type which takes another type as an argument. Syntax isn't so important.Pavel S.

1 Answers

4
votes

There's no difference between those two ways to express the fact that Functor2 is parametrized over a type constructor. If you notice, in the latter case, A is just a placeholder, as it hasn't been declared elsewhere.

I generally prefer to use the F[_] syntax as it clarifies we want a type constructor and we don't have accidental overlapping in naming between the placeholder and types as referred within the class: in the latter case, even though they happen to refer to the same type parameter, there is no enforced constraint between the A in the constructor signature and the A in the map method signature, and this can also lead to some confusion regarding the nature of the two A and how they are related to each other.