From 3db57d4cbf4c294d943e3cc4c48f6ca83d5637d3 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sat, 15 Apr 2017 16:14:48 -0400 Subject: [PATCH 1/3] Add tut doc for FunctionK I'm sure that there is more that could be done with this, and I'm very open to recommendations, but I wanted to get something out there. --- docs/src/main/tut/datatypes/functionk.md | 99 ++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 docs/src/main/tut/datatypes/functionk.md diff --git a/docs/src/main/tut/datatypes/functionk.md b/docs/src/main/tut/datatypes/functionk.md new file mode 100644 index 0000000000..3833d5aa09 --- /dev/null +++ b/docs/src/main/tut/datatypes/functionk.md @@ -0,0 +1,99 @@ +--- +layout: docs +title: "FunctionK" +section: "data" +source: "core/src/main/scala/cats/arrow/FunctionK.scala" +scaladoc: "#cats.arrow.FunctionK" +--- +# FunctionK +`FunctionK` is a universal function which operates on kinds. This statement may be easier to understand +if we first step back and talk about ordinary functions. + +## Ordinary Functions +Consider the following scala method: + +```tut:silent +def first(l: List[Int]): Option[Int] = l.headOption +``` + +This isn't a particularly helpful method, but it will work as an example. Instead of writing this as a +method, we could have written this as a function _value_: + +```tut:silent +val first: List[Int] => Option[Int] = l => l.headOption +``` + +And here, `=>` is really just some syntactic sugar for `Function1`, so we could also write that as: + +```tut:silent +val first: Function1[List[Int], Option[Int]] = l => l.headOption +```` + +Let's cut through the syntactic sugar even a little bit further. `Function1` isn't really a special type. +It's just a trait that looks something like this: + +```tut:silent +// we are calling this `MyFunction1` so we don't collide with the actual `Function1` +trait MyFunction1[A, B] { + def apply(a: A): B +} +``` + +So if we didn't mind being a bit verbose, we could have written our function as: + +```tut:silent +val first: Function1[List[Int], Option[Int]] = new Function1[List[Int], Option[Int]] { + def apply(l: List[Int]): Option[Int] = l.headOption +} +``` + +## Abstracting via Generics + +Recall our `first` method: + +```tut:silent +def first(l: List[Int]): Option[Int] = l.headOption +``` + +The astute reader may have noticed that there's really no reason that this method needs to be tied directly to `Int`. We could use generics to make this a bit more general: + +``` +def first[A](l: List[A]): Option[A] = l.headOption +``` + +But how would we represent this new `first` method as a `=>`/`Function1` value? We are looking for something like a type of `List[A] => Option[A] forAll A`, but this isn't valid scala syntax. `Function1` isn't quite the right fit, because its `apply` method doesn't take a generic type parameter. + +## Higher Kinds to the Rescue + +It turns out that we can represent our universal `List` to `Option` transformation with something that looks a bit like `Function1` but +that adds a type parameter to the `apply` method and utilizes higher kinds: + +```tut:silent +trait MyFunctionK[F[_], G[_]] { + def apply[A](fa: F[A]): G[A] +} +``` + +Cats provides this type as `FunctionK` (we used `MyFunctionK` for our example type to avoid confusion). So now we can write `first` as a `FunctionK[List, Option]` value: + +```tut:silent +import cats.arrow.FunctionK + +val first: FunctionK[List, Option] = new FunctionK[List, Option] { + def apply[A](l: List[A]): Option[A] = l.headOption +} +``` + +## Syntactic Sugar + +If the example above looks a bit too verbose for you, the [kind-projector](https://github.com/non/kind-projector) +compiler plugin [provides](https://github.com/non/kind-projector#polymorphic-lambda-values) a more concise syntax. +After adding the plugin to your project, you could write the `first` example as: + +```tut:silent +val first: FunctionK[List, Option] = λ[FunctionK[List, Option]](_.headOption) +``` + +## Use-cases + +`FunctionK` tends to show up when there is abstraction over higher-kinds. For example, interpreters for [free monads](freemonad.html) and [free applicatives](freeapplicative.html) are represented as `FunctionK` instances. From 0beae665b11da1fef32af0e0b1eef212edb3ec3e Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Thu, 20 Apr 2017 08:57:08 -0400 Subject: [PATCH 2/3] FunctionK doc improvements - Clean up sloppy usage of the term "kind" - Mention the `~>` type alias - Show example of fixing the left of `Either` to make it work with `FunctionK`. --- docs/src/main/tut/datatypes/functionk.md | 28 ++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/src/main/tut/datatypes/functionk.md b/docs/src/main/tut/datatypes/functionk.md index 3833d5aa09..f2fe719c8a 100644 --- a/docs/src/main/tut/datatypes/functionk.md +++ b/docs/src/main/tut/datatypes/functionk.md @@ -6,8 +6,11 @@ source: "core/src/main/scala/cats/arrow/FunctionK.scala" scaladoc: "#cats.arrow.FunctionK" --- # FunctionK -`FunctionK` is a universal function which operates on kinds. This statement may be easier to understand -if we first step back and talk about ordinary functions. +A `FunctionK` transforms values from one first-order-kinded type (a type that takes a single type +parameter, such as `List` or `Option`) into another first-order-kinded type. This transformation is +universal, meaning that a `FunctionK[List, Option]` will translate all `List[A]` values into an +`Option[A]` value for all possible types of `A`. This explanation may be easier to understand if we +first step back and talk about ordinary functions. ## Ordinary Functions Consider the following scala method: @@ -94,6 +97,27 @@ After adding the plugin to your project, you could write the `first` example as: val first: FunctionK[List, Option] = λ[FunctionK[List, Option]](_.headOption) ``` +Cats also provides a `~>` type alias for `FunctionK`, so an even more concise version would be: + +```tut:silent +import cats.~> + +val first: List ~> Option = λ[List ~> Option](_.headOption) +``` + +Being able to use `~>` as an alias for `FunctionK` parallels being able to use `=>` as an alias for `Function1`. + ## Use-cases `FunctionK` tends to show up when there is abstraction over higher-kinds. For example, interpreters for [free monads](freemonad.html) and [free applicatives](freeapplicative.html) are represented as `FunctionK` instances. + +## Types with more than one type parameter + +Earlier it was mentioned that `FunctionK` operates on first-order-kinded types (types that take a single type parameter such as `List` or `Option`). It's still possible to use `FunctionK` with types that would normally take more than one type parameter (such as `Either`) if we fix all of the type parameters except for one. For example: + +```tut:silent +type ErrorOr[A] = Either[String, A] + +val errorOrFirst: FunctionK[List, ErrorOr] = + λ[FunctionK[List, ErrorOr]](_.headOption.toRight("ERROR: the list was empty!")) +``` From d77f00ad5f48279629fd7911aad83ca362528275 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Thu, 20 Apr 2017 18:49:03 -0400 Subject: [PATCH 3/3] Add site menu link for FunctionK docs --- docs/src/main/resources/microsite/data/menu.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/src/main/resources/microsite/data/menu.yml b/docs/src/main/resources/microsite/data/menu.yml index 7578bc5332..ed07d9df79 100644 --- a/docs/src/main/resources/microsite/data/menu.yml +++ b/docs/src/main/resources/microsite/data/menu.yml @@ -121,6 +121,10 @@ options: url: datatypes/freemonad.html menu_type: data + - title: FunctionK + url: datatypes/functionk.html + menu_type: data + - title: Kleisli url: datatypes/kleisli.html menu_type: data