-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Docs for Func and AppFunc #4378
base: main
Are you sure you want to change the base?
Changes from 6 commits
deb9ee3
7954176
e0999bf
6036cdd
700099b
a029422
2cebdb6
b8613ff
06def49
e73fe8e
0ea4eff
aee7268
5b0faa9
9e91be0
1af3c3b
c815fe0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,78 @@ | ||||||
# Func | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since |
||||||
|
||||||
Func is a wrapper around a `run` function `a => F[B]` where F is a functor. Given that, the Func data type is equipped with the known `map` function, and a `mapK` function to apply natural transformations (from a `F` Func get an `G` Func). | ||||||
UlisesTorrella marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
The signature: `Func[F[_], A, B]` refers to an `F` functor, `A` source type and `B` target type (`F[B]`). | ||||||
|
||||||
If you are familiar with `Kleisli` you can recognize it has a similar signature: `Kleisli[F[_], -A, B]` and `Func[F[_], A, B]`. Well yes, `Func` is a less restrictive data type that wraps around functors, and only provides basic methods `run`, `map`, and `mapK`, while `Kleisli` is strong enough to provide composition, flatMap, and more. We will see a more useful data type just next with `AppFunc`. | ||||||
UlisesTorrella marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
## Quick example | ||||||
|
||||||
```scala mdoc:silent:nest | ||||||
import cats.data.{ Func, AppFunc } | ||||||
import cats._ | ||||||
|
||||||
val f: Func[List, Int, String] = Func.func((x: Int) => List(x.toString)) | ||||||
|
||||||
val g: Func[List, Int, Option[String]] = f.map((x: String) => if (x=="0") None else Some(x)) | ||||||
|
||||||
val optToList = new (Option ~> List) { | ||||||
def apply[T](opt: Option[T]): List[T] = | ||||||
opt.toList | ||||||
} | ||||||
|
||||||
// We transform the elements of List, of type | ||||||
// Option[String] to List[String] | ||||||
g.map(optToList(_)) | ||||||
// val res0: cats.data.Func[List,Int,List[String]] = ... | ||||||
``` | ||||||
|
||||||
|
||||||
|
||||||
# AppFunc | ||||||
|
||||||
AppFunc extends Func to wrap around a special type of functor: Applicative functors. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May as well link to the section for reference.
Suggested change
|
||||||
|
||||||
With applicative functors we can `compose`, form the `product`, and also `traverse` traversable functors | ||||||
|
||||||
Signature: `AppFunc[F[_], A, B] extends Func[F, A, B]` | ||||||
|
||||||
Now, for the reader familiar with `Kleisli`, we find an even more similar data type. `AppFunc` provides compositions of weaker constraint, allowing to compose `AppFunc[F[_], A, B]` with `AppFunc[G[_], C, A]`. | ||||||
UlisesTorrella marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think these Kleisli call outs are going to help people trying to figure out what AppFunc is for. Have you considered having a comparison to Kleisli section at the end of the article? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey, yes actually I think at the beginning it was at the end but a suggestion was to move it to the beginning. Given that most doubts in the PR introducing the code were around "why do we need this if Kleisli is a thing" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ha, I guess that's down to a matter of audience. For cats newcomers, Kleisli is probably at least as foreign as this. |
||||||
## Composition | ||||||
|
||||||
All of functional programming revolves around composing, and functors cannot be left behind. If we are working with multiple contexts we might want to compose them, for example: we want to `List` things, and discard some (`Option`). | ||||||
|
||||||
To achieve this nested context behavior `AppFunc` uses the `Nested` datatype. | ||||||
UlisesTorrella marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
```scala mdoc:silent:nest | ||||||
val AppFuncF: AppFunc[Option,Int,Int] = Func.appFunc((i: Int) => if (i==0) None else Some(i)) | ||||||
|
||||||
val AppFuncG: AppFunc[List,Int,Int] = Func.appFunc((o: Int) => {List(o+1)}) | ||||||
UlisesTorrella marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
(AppFuncF andThen AppFuncG).run(1) //Nested(Some(List(2))) | ||||||
|
||||||
(AppFuncF andThen AppFuncG).run(0) //Nested(None) | ||||||
// same thing with compose | ||||||
|
||||||
(AppFuncG compose AppFuncF) | ||||||
``` | ||||||
## Product | ||||||
|
||||||
Applicative functors, like monads, are closed under product. Cats models this data type as Tuple2k, allowing to form the product of two applicative functors (they can be different!) in one data type. | ||||||
UlisesTorrella marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
For further reading: [hearding cats](http://eed3si9n.com/herding-cats/combining-applicative.html#Product+of+applicative+functions) | ||||||
UlisesTorrella marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
```scala mdoc:silent:nest | ||||||
(AppFuncF product AppFuncG).run(1) | ||||||
``` | ||||||
## Traverse | ||||||
|
||||||
This explained in the implementation of the Applicative trait: [Applicative - Traverse](https://typelevel.org/cats/typeclasses/applicative.html#traverse) | ||||||
|
||||||
```scala mdoc:silent:nest | ||||||
AppFuncF.traverse(List(1,2,3)) | ||||||
// val res14: Option[List[Int]] = Some(List(1, 2, 3)) | ||||||
|
||||||
AppFuncF.traverse(List(1,2,0)) | ||||||
//val res15: Option[List[Int]] = None | ||||||
``` | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A link to the scaladoc should be added now that #4401 is merged.