2

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?

joesan
  • 13,963
  • 27
  • 95
  • 232
  • [This question](http://stackoverflow.com/questions/6246719/what-is-a-higher-kinded-type-in-scala) explains higher kinded types in simple terms which answer exactly what you're asking. – Yuval Itzchakov Apr 17 '17 at 11:23

1 Answers1

7

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).

slouc
  • 9,508
  • 3
  • 16
  • 41
  • I still could not notice the subtlety that differentiates the higher kinded type against the Monoid example that I have! – joesan Apr 17 '17 at 10:55
  • Monad could never work with just M, it needs either M[_] or M[B], but with latter you lose flexibility. Former allows you to say "some type constructor", leaving the actual decision about the concrete type to someone else (in our example the methods op and zero). This type constructor is called the HKT. Monoid on the other hand needs just M; if you tried to force the Monoid to use the HKT, you could never have a `Monoid[Int]`. – slouc Apr 17 '17 at 10:58
  • @sparkr There's a difference. You can have a `Monoid[List[Int]]` but not a `Monoid[List]`. You can have a `Monad[List]` but not a `Monad[List[Int]]`. A `Monoid[List[Int]]` only works for lists of integers. A `Monad[List]` works for any list regardless of what's inside. – Jasper-M Apr 17 '17 at 11:32
  • 1
    @Yuval You're right, I messed up the terminology at that part. See edit. – slouc Apr 17 '17 at 11:42
  • @slouc Cheers :) – Yuval Itzchakov Apr 17 '17 at 11:45
  • Hey, I thought the question was closed. I answered your edit part. – slouc Apr 18 '17 at 10:35
  • I'm not talking about Monoid[List[Int]], but Monoid[List[A]], so is my Monoid trait a HKT? – joesan Apr 18 '17 at 15:37
  • 1
    It doesn't matter. It's like saying "i'm not talking about foo(42), but foo(someVariableWhichMayBeOfAnyValueAtRuntime)". Neither are functions, but function invocations. `Monoid` is a type constructor. `Monoid[SomeType]` is a concrete type. Once you provide Monoid with a type, it produces a concrete type. The type you provide can be `List[Int]` or `List[A]` or `F[A]` or `List[Set[Option[Int]]]` or whatever. Always look at the point of **definition** of `Monoid`. If it's defined as `Monoid[A]`, it's first-order (unary) type-constructor. If it's `Monoid[F[A]]` or `Monoid[F[_]]`, it's HKT. – slouc Apr 18 '17 at 16:31
  • Look at the line in my answer `object ListMonad extends Monad[List]`. See that `List` over there? That's the type constructor. It didn't say `List[Int]` or `List[A]` or `List[Set[Option[Whatever]]]`. Those are all applications of the type constructor. But monad is not parameterized by a concrete type; it's parameterized by a *first-order type constructor*. Hence we provide it with just the List. – slouc Apr 18 '17 at 16:36
  • That clarified my doubts! – joesan Apr 18 '17 at 17:28
  • @sparkr Thanks also to you for the question (even if it's a duplicate) because I also clarified some stuff on my side that I never realized I wasn't 100% clear about. It took us a lot of writing and editing and commenting but I think we're good now? :) – slouc Apr 18 '17 at 18:00
  • We are definitely and I thank you for the details! – joesan Apr 18 '17 at 19:02