From 19660616e6b2dc19da06a08f7d01f486d6c4810b Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sat, 23 Jul 2016 15:39:46 -0400 Subject: [PATCH 1/2] Add ListT section to FAQ --- docs/src/main/tut/faq.md | 42 ++++++++++++++++++++++++++++++++++++++++ project/plugins.sbt | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/docs/src/main/tut/faq.md b/docs/src/main/tut/faq.md index b974f8e5f7..21df69b359 100644 --- a/docs/src/main/tut/faq.md +++ b/docs/src/main/tut/faq.md @@ -11,6 +11,7 @@ section: "faq" * [What imports do I need?](#what-imports) * [Why can't the compiler find implicit instances for Future?](#future-instances) * [How can I turn my List of `` into a `` of a list?](#traverse) + * [Where is `ListT`?](#listt) * [What does `@typeclass` mean?](#simulacrum) * [What do types like `?` and `λ` mean?](#kind-projector) * [What does `macro Ops` do? What is `cats.macros.Ops`?](#machinist) @@ -36,6 +37,47 @@ If you have already followed the [imports advice](#what-imports) but are still g It's really common to have a `List` of values with types like `Option`, `Xor`, or `Validated` that you would like to turn "inside out" into an `Option` (or `Xor` or `Validated`) of a `List`. The `sequence`, `sequenceU`, `traverse`, and `traverseU` methods are _really_ handy for this. You can read more about them in the [Traverse documentation]({{ site.baseurl }}/tut/traverse.html). +## Where is ListT? + +There are monad transformers for various types, such as [OptionT]({{ site.baseurl }}/tut/optiont.html), so people often wonder why there isn't a `ListT`. For example, in the following example, people might reach for `ListT` to simplify making nested `map` and `exists` calls: + +```tut:reset:silent +val l: Option[List[Int]] = Some(List(1, 2, 3, 4, 5)) + +def isEven(i: Int): Boolean = i % 2 == 0 +``` + +```tut:book +l.map(_.map(_ + 1)) +l.exists(_.exists(isEven)) +``` + +A naive implementation of `ListT` suffers from associativity issues; see [this gist](https://gist.github.com/tpolecat/1227e22e3161b5816e014c00650f3b57) for an example. It's possible to create a `ListT` that doesn't have these issues, but it tends to be pretty inefficient. For many use-cases, [Nested](https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/data/Nested.scala) can be used to achieve the desired results. + +Here is how we could achieve the effect ofthe previous example using `Nested`: + +```tut:silent +import cats.data.Nested +import cats.implicits._ +``` + +```tut:book +val nl = Nested(l) +nl.map(_ + 1) +nl.exists(isEven) +``` + +We can even perform more complicated operations, such as a `traverse` of the nested structure: + +```tut:silent +import cats.data.ValidatedNel +type ErrorsOr[A] = ValidatedNel[String, A] +def even(i: Int): ErrorsOr[Int] = if (i % 2 == 0) i.validNel else s"$i is odd".invalidNel + +```tut:book +nl.traverse(even) +``` + ## What does `@typeclass` mean? Cats defines and implements numerous type classes. Unfortunately, encoding these type classes in Scala can incur a large amount of boilerplate. To address this, [Simulacrum](https://github.com/mpilquist/simulacrum) introduces `@typeclass`, a macro annotation which generates a lot of this boilerplate. This elevates type classes to a first class construct and increases the legibility and maintainability of the code. Use of simulacrum also ensures consistency in how the type classes are encoded across a project. Cats uses simulacrum wherever possible to encode type classes, and you can read more about it at the [project page](https://github.com/mpilquist/simulacrum). diff --git a/project/plugins.sbt b/project/plugins.sbt index a39fcf3fa5..01c472928a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,7 +4,7 @@ addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.9") addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.3") addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.8.1") -addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.2") +addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.3") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.3") addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0") From e3e4f42941b41ae3bf1508674d4e9fbfa09a725c Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sun, 24 Jul 2016 07:43:50 -0400 Subject: [PATCH 2/2] Fix minor typo in ListT FAQ --- docs/src/main/tut/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/tut/faq.md b/docs/src/main/tut/faq.md index 21df69b359..5087abddb4 100644 --- a/docs/src/main/tut/faq.md +++ b/docs/src/main/tut/faq.md @@ -54,7 +54,7 @@ l.exists(_.exists(isEven)) A naive implementation of `ListT` suffers from associativity issues; see [this gist](https://gist.github.com/tpolecat/1227e22e3161b5816e014c00650f3b57) for an example. It's possible to create a `ListT` that doesn't have these issues, but it tends to be pretty inefficient. For many use-cases, [Nested](https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/data/Nested.scala) can be used to achieve the desired results. -Here is how we could achieve the effect ofthe previous example using `Nested`: +Here is how we could achieve the effect of the previous example using `Nested`: ```tut:silent import cats.data.Nested