- FunctionK

Cats provides `FunctionK`

that accepts two type constructors `F1[_]`

and `F2[_]`

as type parameter that can transform all values in `F1[A]`

to `F2[A]`

for all `A`

.

```
trait FunctionK[F[_], G[_]] extends Serializable { self =>
/**
* Applies this functor transformation from `F` to `G`
*/
def apply[A](fa: F[A]): G[A]
def compose[E[_]](f: FunctionK[E, F]): FunctionK[E, G] =
new FunctionK[E, G] { def apply[A](fa: E[A]): G[A] = self(f(fa)) }
def andThen[H[_]](f: FunctionK[G, H]): FunctionK[F, H] =
f.compose(self)
def or[H[_]](h: FunctionK[H, G]): FunctionK[EitherK[F, H, *], G] =
new FunctionK[EitherK[F, H, *], G] { def apply[A](fa: EitherK[F, H, A]): G[A] = fa.fold(self, h) }
....
}
```

`FunctionK[F1, F2]`

is denoted symbolically as `F1 ~> F2`

:

```
import cats._, cats.syntax.all._
lazy val first: List ~> Option = ???
```

Because we tend to call `F[_]`

as functors, sometimes `FunctionK`

is aspirationally called a *natural transformation*, but I think `FunctionK`

is a better name for what it does.

Let’s try implementing `List ~> Option`

that returns the first element.

```
val first: List ~> Option = new (List ~> Option) {
def apply[A](fa: List[A]): Option[A] = fa.headOption
}
// first: List ~> Option = repl.MdocSession1@331e0beb
first(List("a", "b", "c"))
// res1: Option[String] = Some(value = "a")
```

It looks a bit verbose. Depending on how often this shows up in the code, we might want a way to write it shorter like how we’re usually able to write:

```
import scala.util.chaining._
List("a", "b", "c").pipe(_.headOption)
// res2: Option[String] = Some(value = "a")
```

We can do this using *polymorphic lambda rewrite* `λ`

provided by the kind projector:

```
val first = λ[List ~> Option](_.headOption)
// first: AnyRef with List ~> Option = repl.MdocSession2@16c1db63
first(List("a", "b", "c"))
// res4: Option[String] = Some(value = "a")
```

In July of 2010, Rúnar (@runarorama) wrote a blog post Higher-Rank Polymorphism in Scala, describing the concept of rank-2 polymorphism. First, here’s an ordinary (rank-1) polymorphic function:

```
def pureList[A](a: A): List[A] = List(a)
```

This would work for any `A`

:

```
pureList(1)
// res5: List[Int] = List(1)
pureList("a")
// res6: List[String] = List("a")
```

What Rúnar pointed out in 2010 is that Scala does not have a first-class notion for this.

Now say we want to take such a function as an argument to another function. With just rank-1 polymorphism, we can’t do this:

```
def usePolyFunc[A, B](f: A => List[A], b: B, s: String): (List[B], List[String]) =
(f(b), f(s))
// error: type mismatch;
// found : b.type (with underlying type B)
// required: A
// (f(b), f(s))
// ^
// error: type mismatch;
// found : s.type (with underlying type String)
// required: A
// (f(b), f(s))
// ^
```

This is also what Launchbury and SPJ pointed out that Haskell cannot do in State Threads in 1994:

```
runST :: ∀a. (∀s. ST s a) -> a
```

This is not a Hindley-Milner type, because the quantifiers are not all at the top level; it is an example of rank-2 polymorphism.

Back to Rúnar:

It’s a type error because,

`B`

and`String`

are not`A`

. That is, the type`A`

is fixed on the right of the quantifier`[A, B]`

. We really want the polymorphism of the argument to be maintained so we can apply it polymorphically in the body of our function. Here’s how that might be expressed if Scala had rank-n types:

```
def usePolyFunc[B](f: (A => List[A]) forAll { A }, b: B, s: String): (List[B], List[String]) =
(f(b), f(s))
```

So what we do is represent a rank-2 polymorphic function with a new trait that accepts a type argument in its

`apply`

method:

```
trait ~>[F[_], G[_]] {
def apply[A](a: F[A]): G[A]
}
```

This is the same as `FunctionK`

, or `FunctionK`

is the same as `~>`

. Next, in a brilliant move Rúnar lifts `A`

to `F[_]`

using Id datatype:

We can now model a function that takes a value and puts it in a list, as a natural transformation from the identity functor to the List functor:

```
val pureList: Id ~> List = λ[Id ~> List](List(_))
// pureList: Id ~> List = repl.MdocSession3@6e515b56
def usePolyFunc[B](f: Id ~> List, b: B, s: String): (List[B], List[String]) =
(f(b), f(s))
usePolyFunc(pureList, 1, "x")
// res9: (List[Int], List[String]) = (List(1), List("x"))
```

Yes. We now managed to pass polymorphic function around. I am guessing that rank-2 polymorphism was all the rage in part because it was advertised as the foundation to ensure typesafe access to resources in State Threads and other papers that came after it.

If we look at MonadCancel again, there’s `FunctionK`

:

```
trait MonadCancel[F[_], E] extends MonadError[F, E] {
def rootCancelScope: CancelScope
def forceR[A, B](fa: F[A])(fb: F[B]): F[B]
def uncancelable[B](body: Poll[F] => F[B]): F[B]
....
}
```

In the above, `Poll[F]`

is actually a type alias for `F ~> F`

:

```
trait Poll[F[_]] extends (F ~> F)
```

In other words, for all `A`

`F[A]`

would return `F[A]`

.

```
import cats.effect.IO
lazy val program = IO.uncancelable { poll =>
poll(IO.canceled) >> IO.println("nope again")
}
```

In the above, `IO`

must give us a function that works for any type `A`

, and as we know from Rúnar’s post rank-1 polymorphism won’t work. Imagine if it were:

```
def uncancelable[A, B](body: F[A] => F[A] => F[B]): F[B]
```

This might work one call of `poll(...)`

, but within `IO.uncancelable { ... }`

you should be able to call `poll(...)`

multiple times:

```
lazy val program2: IO[Int] = IO.uncancelable { poll =>
poll(IO.println("a")) >> poll(IO.pure("b")) >> poll(IO.pure(1))
}
```

So really `poll(...)`

is `∀A. IO[A] => IO[A]`

, or `IO ~> IO`

.

herding cats — FunctionK