-
Notifications
You must be signed in to change notification settings - Fork 450
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
kategory-docs
module and proposed layout including Functor
and Applicative
#191
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
94ded8d
`kategory-docs` module and proposed layout for typeclasses and dataty…
raulraja be25864
Add gradle.properties to docs module
raulraja 9291608
Merge branch 'master' into rr-docs-draft
raulraja 68a6573
Applicative docs
raulraja ff8fdc5
Merge remote-tracking branch 'origin/rr-docs-draft' into rr-docs-draft
raulraja 20d06cb
Merge branch 'master' into rr-docs-draft
raulraja 857ea21
add ank
Guardiola31337 424aa81
Merge branch 'master' into rr-docs-draft
raulraja ee33a2d
reorg files
raulraja 2cb24b4
bump ank-gradle-plugin version to 0.1.3, kotlinx coroutines version t…
Guardiola31337 ca148aa
Merge branch 'master' into rr-docs-draft
raulraja bf6c09c
fixes docs code snippets and excludes kategory since it is brought in…
raulraja 994e6f3
Merge remote-tracking branch 'origin/rr-docs-draft' into rr-docs-draft
raulraja 63767bb
Build docs on CI
raulraja ceb7ec6
bump ank-gradle-plugin dependency to 0.1.4
Guardiola31337 74d34bc
Merge remote-tracking branch 'origin/master' into rr-docs-draft
raulraja 4434e87
Add note on `Applicative` addressing https://github.com/kategory/kate…
raulraja 3886388
add clarification that laws are actual test cases
raulraja File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
apply plugin: 'ank-gradle-plugin' | ||
|
||
dependencies { | ||
//compile project(':kategory') | ||
//compile project(':kategory-effects') | ||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVersion" | ||
compile "org.jetbrains.kotlinx:kotlinx-collections-immutable:$kotlinxCollectionsImmutableVersion" | ||
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion" | ||
compile "io.kotlintest:kotlintest:$kotlinTestVersion" | ||
} | ||
|
||
ank { | ||
source = file("${projectDir}/docs") | ||
target = file("${projectDir}/build/site") | ||
classpath = sourceSets.main.runtimeClasspath | ||
} | ||
|
||
sourceCompatibility = javaVersion | ||
targetCompatibility = javaVersion | ||
|
||
apply from: rootProject.file('gradle/gradle-mvn-push.gradle') | ||
|
||
compileKotlin.kotlinOptions.freeCompilerArgs += ["-Xskip-runtime-version-check"] |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
--- | ||
layout: docs | ||
title: Applicative | ||
permalink: /docs/typeclasses/applicative/ | ||
--- | ||
|
||
## Applicative | ||
|
||
The `Applicative` typeclass abstracts the ability to lift values and apply functions over the computational context of a type constructor. | ||
Examples of type constructors that can implement instances of the Applicative typeclass include `Option`, `NonEmptyList`, | ||
`List` and many other datatypes that include a `pure` and either `ap` function. `ap` may be derived for monadic types that include a `Monad` instance via `flatMap`. | ||
|
||
`Applicative` includes all combinators present in [`Functor`](/docs/typeclasses/functor/). | ||
|
||
### Applicative Builder examples | ||
|
||
Often times we find ourselves in situations where we need to compute multiple independent values resulting from operations that do not depend on each other. | ||
|
||
In the following example we will define 3 invocations that may as well be remote or local services each one of them returning different results in the same computational context of `Option` | ||
|
||
```kotlin:ank | ||
import kategory.* | ||
|
||
fun profileService(): Option<String> = Option("Alfredo Lambda") | ||
fun phoneService(): Option<Int> = Option(55555555) | ||
fun addressService(): Option<List<String>> = Option(listOf("1 Main Street", "11130", "NYC")) | ||
``` | ||
|
||
This more or less illustrate the common use case of performing several independent operations where we need to get all the results together | ||
|
||
Kategory features an [Applicative Builder](/docs/patterns/applicativebuilder) that allows you to easily combine all the independent operations into one result. | ||
|
||
```kotlin:ank | ||
data class Profile(val name: String, val phone: Int, val address: List<String>) | ||
|
||
val r: Option<Tuple3<String, Int, List<String>>> = Option.applicative().tupled(profileService(), phoneService(), addressService()).ev() | ||
r.map { Profile(it.a, it.b, it.c) } | ||
``` | ||
|
||
The Applicative Builder also provides a `map` operations that is able to abstract over arity in the same way as `tupled` | ||
|
||
```kotlin:ank | ||
Option.applicative().map(profileService(), phoneService(), addressService(), { (name, phone, addresses) -> | ||
Profile(name, phone, addresses) | ||
}) | ||
``` | ||
|
||
### Main Combinators | ||
|
||
#### pure | ||
|
||
Lifts a value into the computational context of a type constructor | ||
|
||
`fun <A> pure(a: A): HK<F, A>` | ||
|
||
```kotlin:ank | ||
Option.pure(1) // Option(1) | ||
``` | ||
|
||
#### ap | ||
|
||
Apply a function inside the type constructor's context | ||
|
||
`fun <A, B> ap(fa: HK<F, A>, ff: HK<F, (A) -> B>): HK<F, B>` | ||
|
||
```kotlin:ank | ||
Option.applicative().ap(Option(1), Option({ n: Int -> n + 1 })) // Option(2) | ||
``` | ||
|
||
#### Other combinators | ||
|
||
For a full list of other useful combinators available in `Applicative` see the [`KDoc](/kdocs/typeclasses/applicative) | ||
|
||
### Syntax | ||
|
||
#### HK<F, A>#pure | ||
|
||
Lift a value into the computational context of a type constructor | ||
|
||
```kotlin:ank | ||
1.pure<OptionHK, Int>() | ||
``` | ||
|
||
#### HK<F, A>#ap | ||
|
||
Apply a function inside the type constructor's context | ||
|
||
```kotlin:ank | ||
Option(1).ap(Option({ n: Int -> n + 1 })) | ||
``` | ||
|
||
#### HK<F, A>#map2 | ||
|
||
Map 2 values inside the type constructor context and apply a function to their cartesian product | ||
|
||
```kotlin:ank | ||
Option.applicative().map2(Option(1), Option("x"), { z: Tuple2<Int, String> -> "${z.a}${z.b}" }) | ||
``` | ||
|
||
#### HK<F, A>#map2Eval | ||
|
||
Lazily map 2 values inside the type constructor context and apply a function to their cartesian product. | ||
Computation happens when `.value()` is invoked. | ||
|
||
```kotlin:ank | ||
Option.applicative().map2Eval(Option(1), Eval.later { Option("x") }, { z: Tuple2<Int, String> -> "${z.a}${z.b}" }).value() | ||
``` | ||
|
||
|
||
### Laws | ||
|
||
Kategory provides [`ApplicativeLaws`](/docs/typeclasses/laws#applicativelaws) in the form of test cases for internal verification of lawful instances and third party apps creating their own Applicative instances. | ||
|
||
#### Creating your own `Applicative` instances | ||
|
||
[Kategory already provides Applicative instances for most common datatypes](#datatypes) both in Kategory and the Kotlin stdlib. | ||
See [Deriving and creating custom typeclass] to provide your own Applicative instances for custom datatypes. | ||
|
||
### Data types | ||
|
||
Thw following datatypes in Kategory provide instances that adhere to the `Applicative` typeclass. | ||
|
||
- [Either](/docs/datatypes/either) | ||
- [EitherT](/docs/datatypes/eitherT) | ||
- [FreeApplicative](/docs/datatypes/FreeApplicative) | ||
- [Function1](/docs/datatypes/Function1) | ||
- [Ior](/docs/datatypes/Ior) | ||
- [Kleisli](/docs/datatypes/Kleisli) | ||
- [OptionT](/docs/datatypes/OptionT) | ||
- [StateT](/docs/datatypes/StateT) | ||
- [Validated](/docs/datatypes/Validated) | ||
- [WriterT](/docs/datatypes/WriterT) | ||
- [Const](/docs/datatypes/Const) | ||
- [Try](/docs/datatypes/Try) | ||
- [Eval](/docs/datatypes/Eval) | ||
- [IO](/docs/datatypes/IO) | ||
- [NonEmptyList](/docs/datatypes/NonEmptyList) | ||
- [Id](/docs/datatypes/Id) | ||
- [Function0](/docs/datatypes/Function0) | ||
|
||
Additionally all instances of [`Monad`](/docs/typeclasses/monad) and their MTL variants implement the `Applicative` typeclass directly | ||
since they are all subtypes of `Applicative` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
--- | ||
layout: docs | ||
title: Functor | ||
permalink: /docs/typeclasses/functor/ | ||
--- | ||
|
||
## Functor | ||
|
||
The `Functor` typeclass abstracts the ability to `map` over the computational context of a type constructor. | ||
Examples of type constructors that can implement instances of the Functor typeclass include `Option`, `NonEmptyList`, | ||
`List` and many other datatypes that include a `map` function with the shape `fun F<B>.map(f: (A) -> B): F<B>` where `F` | ||
refers to `Option`, `List` or any other type constructor whose contents can be transformed. | ||
|
||
### Example | ||
|
||
Often times we find ourselves in situations where we need to transform the contents of some datatype. `Functor#map` allows | ||
us to safely compute over values under the assumption that they'll be there returning the transformation encapsulated in the same context. | ||
|
||
Consider both `Option` and `Try`: | ||
|
||
`Option<A>` allows us to model absence and has two possible states, `Some(a: A)` if the value is not absent and `None` to represent an empty case. | ||
|
||
In a similar fashion `Try<A>` may have two possible cases `Success(a: A)` for computations that succeed and `Failure(e: Throwable)` if they fail with an exception. | ||
|
||
Both `Try` and `Option` are example datatypes that can be computed over transforming their inner results. | ||
|
||
```kotlin:ank | ||
import kategory.* | ||
|
||
Try { "1".toInt() }.map { it * 2 } | ||
Option(1).map { it * 2 } | ||
``` | ||
|
||
Both `Try` and `Option` include ready to use `Functor` instances: | ||
|
||
```kotlin:ank | ||
val optionFunctor = Option.functor() | ||
``` | ||
|
||
```kotlin:ank | ||
val tryFunctor = Try.functor() | ||
``` | ||
|
||
Mapping over the empty/failed cases is always safe since the `map` operation in both Try and Option operate under the bias of those containing success values | ||
|
||
```kotlin:ank | ||
Try { "x".toInt() }.map { it * 2 } | ||
none<Int>().map { it * 2 } | ||
``` | ||
|
||
Kategory allows abstract polymorphic code that operates over the evidence of having an instance of a typeclass available. | ||
This enables programs that are not coupled to specific datatype implementations. | ||
The technique demonstrated below to write polymorphic code is available for all other [Typeclasses](/docs/typeclasses) beside `Functor`. | ||
|
||
```kotlin:ank | ||
inline fun <reified F> multiplyBy2(fa: HK<F, Int>, FT: Functor<F> = functor()): HK<F, Int> = | ||
FT.map(fa, { it * 2 }) | ||
|
||
multiplyBy2<OptionHK>(Option(1)) // Option(1) | ||
multiplyBy2<TryHK>(Try { 1 }) | ||
``` | ||
|
||
In the example above we've defined a function that can operate over any data type for which a `Functor` instance is available. | ||
And then we applied `multiplyBy2` to two different datatypes for which Functor instances exist. | ||
This technique applied to other Typeclasses allows users to describe entire programs in terms of behaviors typeclasses removing | ||
dependencies to concrete data types and how they operate. | ||
|
||
This technique does not enforce inheritance or any kind of subtyping relationship and is frequently known as [`ad-hoc polymorphism`](https://en.wikipedia.org/wiki/Ad_hoc_polymorphism) | ||
and frequently used in programming languages that support [Typeclasses](https://en.wikipedia.org/wiki/Type_class) and [Higher Kinded Types](https://en.wikipedia.org/wiki/Kind_(type_theory)). | ||
|
||
Entire libraries and applications can be written without enforcing consumers to use the lib author provided datatypes but letting | ||
users provide their own provided there is typeclass instances for their datatypes. | ||
|
||
### Main Combinators | ||
|
||
#### map | ||
|
||
Transforms the inner contents | ||
|
||
`fun <A, B> map(fa: HK<F, A>, f: (A) -> B): HK<F, B>` | ||
|
||
```kotlin:ank | ||
optionFunctor.map(Option(1), { it + 1 }) | ||
``` | ||
|
||
#### lift | ||
|
||
Lift a function to the Functor context so it can be applied over values of the implementing datatype | ||
|
||
`fun <A, B> lift(f: (A) -> B): (HK<F, A>) -> HK<F, B>` | ||
|
||
```kotlin:ank | ||
val lifted = optionFunctor.lift({ n: Int -> n + 1 }) | ||
lifted(Option(1)) | ||
``` | ||
|
||
#### Other combinators | ||
|
||
For a full list of other useful combinators available in `Functor` see the [`KDoc](/kdocs/typeclasses/functor) | ||
|
||
### Syntax | ||
|
||
#### HK<F, A>#map | ||
|
||
Maps over any higher kinded type constructor for which a functor instance is found | ||
|
||
```kotlin:ank | ||
Try { 1 }.map({ it + 1 }) | ||
``` | ||
|
||
#### ((A) -> B)#lift | ||
|
||
Lift a function into the functor context | ||
|
||
```kotlin:ank | ||
val f = { n: Int -> n + 1 }.lift<OptionHK, Int, Int>() | ||
f(Option(1)) | ||
``` | ||
|
||
|
||
### Laws | ||
|
||
Kategory provides [`FunctorLaws`](/docs/typeclasses/laws#functorlaws) in the form of test cases for internal verification of lawful instances and third party apps creating their own Functor instances. | ||
|
||
#### Creating your own `Functor` instances | ||
|
||
[Kategory already provides Functor instances for most common datatypes](#datatypes) both in Kategory and the Kotlin stdlib. | ||
Often times you may find the need to provide your own for unsupported datatypes. | ||
|
||
You may create or automatically derive instances of functor for your own datatypes which you will be able to use in the context of abstract polymorfic code | ||
as demonstrated in the [example](#example) above. | ||
|
||
See [Deriving and creating custom typeclass] | ||
|
||
### Data types | ||
|
||
Thw following datatypes in Kategory provide instances that adhere to the `Functor` typeclass. | ||
|
||
- [Cofree](/docs/datatypes/cofree) | ||
- [Coproduct](/docs/datatypes/coproduct) | ||
- [Coyoneda](/docs/datatypes/coyoneda) | ||
- [Either](/docs/datatypes/either) | ||
- [EitherT](/docs/datatypes/eitherT) | ||
- [FreeApplicative](/docs/datatypes/FreeApplicative) | ||
- [Function1](/docs/datatypes/Function1) | ||
- [Ior](/docs/datatypes/Ior) | ||
- [Kleisli](/docs/datatypes/Kleisli) | ||
- [OptionT](/docs/datatypes/OptionT) | ||
- [StateT](/docs/datatypes/StateT) | ||
- [Validated](/docs/datatypes/Validated) | ||
- [WriterT](/docs/datatypes/WriterT) | ||
- [Yoneda](/docs/datatypes/Yoneda) | ||
- [Const](/docs/datatypes/Const) | ||
- [Try](/docs/datatypes/Try) | ||
- [Eval](/docs/datatypes/Eval) | ||
- [IO](/docs/datatypes/IO) | ||
- [NonEmptyList](/docs/datatypes/NonEmptyList) | ||
- [Id](/docs/datatypes/Id) | ||
- [Function0](/docs/datatypes/Function0) | ||
|
||
Additionally all instances of [`Applicative`](/docs/typeclasses/applicative), [`Monad`](/docs/typeclasses/monad) and their MTL variants implement the `Functor` typeclass directly | ||
since they are all subtypes of `Functor` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Maven publishing configuration | ||
POM_NAME=Kategory-Docs | ||
POM_ARTIFACT_ID=kategory-docs | ||
POM_PACKAGING=jar |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This is the most generic example of the API. We should display some implementation examples below, i.e. the same for Option or Either. Not the implementation, just the signature with the generics pre-applied.
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.
There are examples below of usage. Adding a concrete form using for example
OptionKind
it's going to generate more confusion since we will have to explain here what kinds are. This can be better addressed in the derivation section or a section where we explain how we use higher kinds and how we have emulated them.