Higher Kinded Types in Scala [duplicate]

拈花ヽ惹草 提交于 2020-01-12 10:45:13

问题


I'm reading through the Functional Programming in Scala book and in the Monoids chapter, they talk about a Monoid interface that looks like this:

trait Monoid[A] {
 def op(a1: A, a2: A): A
 def zero: A
}

Later on, they define specific Monoid instances by extending this interface. For example.,

val intMonoid = new Monoid[Int] {
  ...
}

val listMonoid = new Monoid[List[Int]] {
  ...
}

A couple more pages that I read through this chapter 10, I come across 'higher kinded types' which according to the book is any type that it self is a type that can take other types.

trait Foldable[F[_]] {
 ...
 ...
}

So the trait Foldable is according to the book a higher kinded type. My question is, the Monoid[A] to me is also fits the 'higher kinded type' definition as it can take a List[A]. Is my understanding correct? If not what makes higher kinded types a higher kinded type in Scala?

Edit: So a unary type constructor takes an argument and produces a type. Now what about this case here?

def listMonoid[A] = new Monoid[List[A]] {
  ...
  ...
}

So is my listMonoid function a HKT?


回答1:


Some terminology:

  • proper type (e.g. Int)
  • first-order type (e.g. List[_]); we could also say first-order kind
  • higher-kinded type (e.g. Monad[M[_])

When you say

trait Monoid[A] {
  def op(a1: A, a2: A): A
  def zero: A
}

val listMonoid = new Monoid[List[Int]] {
  def op(l: List[Int], l2: List[Int]) =  List(1,2)
  def zero = List(1,2)
}

you are parameterizing the Monoid trait with some type A, which can (as you noticed) be a simple type, also know as proper type (e.g. Int) or a parameterized type (e.g. List[Int], or even List[Set[Map[Int, Int]]). This makes Monoid a first-order type. We can also say that it's a unary type constructor - it takes one type to produce the final type.

Unlike Monoid, some abstractions (e.g. Monad) need to be parameterized by a type constructor. Int doesn't work any more. It needs to be "some type than can produce another type". Abstraction which is parameterized by a type constructor (that is, parameterized by a "first-order type") is a higher-kinded type. Here's an example:

trait Monad[M[_]] {
  def op[A, B](m: M[A], f: A => M[B]): M[B]
  def zero[A](a: A): M[A]
}

object ListMonad extends Monad[List] {
  def op[A, B](m: List[A], f: A => List[B]) = m.flatMap(f)
  def zero[A](a: A) = List[A](a)
}

val listMonad = ListMonad.zero(42)
val result = ListMonad.op(listMonad, (i: Int) => List(i - 1, i, i + 1))

// result = List(41, 42, 43)

So Monad is parameterized by a first-order type (a unary type constructor), which makes Monad itself a higher-kinded type.

Note how Monad doesn't really care about the "inner type" itself on the class level as it will be defined by the methods op and zero. You could also say trait Monad[M[A]] and "fix" the type A at the point of definition of class ListMonad (e.g. fix it to Int), but then you are losing flexibility (your ListMonad will then only be able to construct and flatMap a List[Int] and you would need a different class for, say, List[String]).

This is different than a Monoid which is not a higher-kinded type; it doesn't need a type constructor to produce a type. If it needed it, then you could never have a, say, Monoid[Int], because Int is not a type constructor.

Note also how I said that Monad needs a unary type constructor, meaning it takes only one type (unlike e.g. Map which takes two). Type constructors are often represented with asterisks and arrows:

  • unary first-order type constructor is * -> * (it takes a single type and produces the final type, e.g. Set)
  • binary first-order type constructor is * -> * -> * (a binary type constructor, takes two types to produce the final type, e.g. Map)
  • unary higher-kinded type is (* -> *) -> * (takes a single unary type constructor to produce the final type, e.g. Monad)

etc.

So, first-order type takes a simple/concrete/proper type and produces the final type, while higher-kinded type goes one level above; it takes a first-order type to produce the final type.

EDIT:

Answering your question in "edit" part: OK I think I know what's confusing you. listMonoid is not a type, so it can't be a higher-kinded type. It's a method. Monad[List[Int]] is a fully resolved type. Monad[F[A]] is also fully resolved. However, Monad itself is a higher order type.

Let me pull the parallel to functions. If you have a function foo(x: Int), then function invocations such foo(42) or foo(someIntegerValue) result in concrete values. These are the analogous to Monad[List[Int]] and Monad[F[A]]. However, foo itself is a function, just like Monad itself is a type constructor.

If foo takes a simple value (not a function), it's a first-order function; if it takes or returns a function, then it's a higher-order function. Same with type constructors. If it takes a simple type, it's a first-order type constructor. Example: List. If it takes another type constructor, it's a higher-order type constructor (also know as higher-kinded type). Example: Monad.

Don't mix resolved types with type constructors. It makes sense to think whether function foo is higher-order or not; this depends on its arguments and return type. But it makes no sense to think whether foo(42) is higher-order or not; this is not a function, but a function application, which results in value. Monad[List[Int]] is not a type constructor, but an application of type constructor List to the type constructor Monad (which is higher-order). Similarly, Monoid[List[Int]] is not a type constrcutor, but an application of type List[Int] to the type constructor Monoid (which is first-order). Type constructors of higher order are called HKT. It doesn't make sense to talk about HKT and point at a concrete resolved type (that was created as a result of application of some type constructor).



来源:https://stackoverflow.com/questions/43448055/higher-kinded-types-in-scala

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