Skip to content

Commit cf053de

Browse files
authored
Merge pull request #191 from kategory/rr-docs-draft
`kategory-docs` module and proposed layout including `Functor` and `Applicative`
2 parents 8e35d6d + 3886388 commit cf053de

File tree

11 files changed

+345
-5
lines changed

11 files changed

+345
-5
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jdk:
66
- oraclejdk8
77

88
script:
9-
- ./gradlew clean build
9+
- ./gradlew clean build :kategory-docs:runAnk
1010
- ./gradlew codeCoverageReport
1111

1212
after_success:

build.gradle

+10-2
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,25 @@ buildscript {
2424
javaVersion = JavaVersion.VERSION_1_7
2525
kotlinTestVersion = '2.0.5'
2626
kotlinVersion = '1.1.4'
27-
kotlinxCoroutinesVersion = '0.17'
27+
kotlinxCoroutinesVersion = '0.18'
2828
kategoryAnnotationsVersion = '0.3.7'
29+
kotlinxCollectionsImmutableVersion = '0.1'
2930
}
3031

3132
repositories {
3233
jcenter()
34+
maven { url 'https://jitpack.io' }
3335
maven { url "http://dl.bintray.com/kotlin/kotlin-dev" }
36+
maven { url "https://dl.bintray.com/jetbrains/markdown/" }
3437
}
3538

3639
dependencies {
3740
classpath "com.github.ben-manes:gradle-versions-plugin:$gradleVersionsPluginVersion"
3841
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
3942
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
43+
classpath('io.kategory:ank-gradle-plugin:0.1.4') {
44+
exclude group: 'io.kategory'
45+
}
4046
}
4147
}
4248

@@ -46,10 +52,12 @@ subprojects { project ->
4652
version = VERSION_NAME
4753

4854
repositories {
49-
maven {url "http://dl.bintray.com/kategory/maven" }
5055
jcenter()
5156
maven { url 'https://kotlin.bintray.com/kotlinx' }
5257
maven { url "http://dl.bintray.com/kotlin/kotlin-dev" }
58+
maven { url "http://dl.bintray.com/kategory/maven" }
59+
maven { url "https://dl.bintray.com/jetbrains/markdown/" }
60+
maven { url 'https://jitpack.io' }
5361
}
5462

5563
apply plugin: 'kotlin'

kategory-docs/README.md

Whitespace-only changes.

kategory-docs/build.gradle

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
apply plugin: 'ank-gradle-plugin'
2+
3+
dependencies {
4+
//compile project(':kategory')
5+
//compile project(':kategory-effects')
6+
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVersion"
7+
compile "org.jetbrains.kotlinx:kotlinx-collections-immutable:$kotlinxCollectionsImmutableVersion"
8+
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion"
9+
compile "io.kotlintest:kotlintest:$kotlinTestVersion"
10+
}
11+
12+
ank {
13+
source = file("${projectDir}/docs")
14+
target = file("${projectDir}/build/site")
15+
classpath = sourceSets.main.runtimeClasspath
16+
}
17+
18+
sourceCompatibility = javaVersion
19+
targetCompatibility = javaVersion
20+
21+
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
22+
23+
compileKotlin.kotlinOptions.freeCompilerArgs += ["-Xskip-runtime-version-check"]

kategory-docs/docs/datatypes/README.md

Whitespace-only changes.

kategory-docs/docs/typeclasses/README.md

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
---
2+
layout: docs
3+
title: Applicative
4+
permalink: /docs/typeclasses/applicative/
5+
---
6+
7+
## Applicative
8+
9+
The `Applicative` typeclass abstracts the ability to lift values and apply functions over the computational context of a type constructor.
10+
Examples of type constructors that can implement instances of the Applicative typeclass include `Option`, `NonEmptyList`,
11+
`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`.
12+
13+
`Applicative` includes all combinators present in [`Functor`](/docs/typeclasses/functor/).
14+
15+
### Applicative Builder examples
16+
17+
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.
18+
19+
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`
20+
21+
```kotlin:ank
22+
import kategory.*
23+
24+
fun profileService(): Option<String> = Option("Alfredo Lambda")
25+
fun phoneService(): Option<Int> = Option(55555555)
26+
fun addressService(): Option<List<String>> = Option(listOf("1 Main Street", "11130", "NYC"))
27+
```
28+
29+
This more or less illustrate the common use case of performing several independent operations where we need to get all the results together
30+
31+
Kategory features an [Applicative Builder](/docs/patterns/applicativebuilder) that allows you to easily combine all the independent operations into one result.
32+
33+
```kotlin:ank
34+
data class Profile(val name: String, val phone: Int, val address: List<String>)
35+
36+
val r: Option<Tuple3<String, Int, List<String>>> = Option.applicative().tupled(profileService(), phoneService(), addressService()).ev()
37+
r.map { Profile(it.a, it.b, it.c) }
38+
```
39+
40+
The Applicative Builder also provides a `map` operations that is able to abstract over arity in the same way as `tupled`
41+
42+
```kotlin:ank
43+
Option.applicative().map(profileService(), phoneService(), addressService(), { (name, phone, addresses) ->
44+
Profile(name, phone, addresses)
45+
})
46+
```
47+
48+
### Main Combinators
49+
50+
#### pure
51+
52+
Lifts a value into the computational context of a type constructor
53+
54+
`fun <A> pure(a: A): HK<F, A>`
55+
56+
```kotlin:ank
57+
Option.pure(1) // Option(1)
58+
```
59+
60+
#### ap
61+
62+
Apply a function inside the type constructor's context
63+
64+
`fun <A, B> ap(fa: HK<F, A>, ff: HK<F, (A) -> B>): HK<F, B>`
65+
66+
```kotlin:ank
67+
Option.applicative().ap(Option(1), Option({ n: Int -> n + 1 })) // Option(2)
68+
```
69+
70+
#### Other combinators
71+
72+
For a full list of other useful combinators available in `Applicative` see the [`KDoc](/kdocs/typeclasses/applicative)
73+
74+
### Syntax
75+
76+
#### HK<F, A>#pure
77+
78+
Lift a value into the computational context of a type constructor
79+
80+
```kotlin:ank
81+
1.pure<OptionHK, Int>()
82+
```
83+
84+
#### HK<F, A>#ap
85+
86+
Apply a function inside the type constructor's context
87+
88+
```kotlin:ank
89+
Option(1).ap(Option({ n: Int -> n + 1 }))
90+
```
91+
92+
#### HK<F, A>#map2
93+
94+
Map 2 values inside the type constructor context and apply a function to their cartesian product
95+
96+
```kotlin:ank
97+
Option.applicative().map2(Option(1), Option("x"), { z: Tuple2<Int, String> -> "${z.a}${z.b}" })
98+
```
99+
100+
#### HK<F, A>#map2Eval
101+
102+
Lazily map 2 values inside the type constructor context and apply a function to their cartesian product.
103+
Computation happens when `.value()` is invoked.
104+
105+
```kotlin:ank
106+
Option.applicative().map2Eval(Option(1), Eval.later { Option("x") }, { z: Tuple2<Int, String> -> "${z.a}${z.b}" }).value()
107+
```
108+
109+
110+
### Laws
111+
112+
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.
113+
114+
#### Creating your own `Applicative` instances
115+
116+
[Kategory already provides Applicative instances for most common datatypes](#datatypes) both in Kategory and the Kotlin stdlib.
117+
See [Deriving and creating custom typeclass] to provide your own Applicative instances for custom datatypes.
118+
119+
### Data types
120+
121+
Thw following datatypes in Kategory provide instances that adhere to the `Applicative` typeclass.
122+
123+
- [Either](/docs/datatypes/either)
124+
- [EitherT](/docs/datatypes/eitherT)
125+
- [FreeApplicative](/docs/datatypes/FreeApplicative)
126+
- [Function1](/docs/datatypes/Function1)
127+
- [Ior](/docs/datatypes/Ior)
128+
- [Kleisli](/docs/datatypes/Kleisli)
129+
- [OptionT](/docs/datatypes/OptionT)
130+
- [StateT](/docs/datatypes/StateT)
131+
- [Validated](/docs/datatypes/Validated)
132+
- [WriterT](/docs/datatypes/WriterT)
133+
- [Const](/docs/datatypes/Const)
134+
- [Try](/docs/datatypes/Try)
135+
- [Eval](/docs/datatypes/Eval)
136+
- [IO](/docs/datatypes/IO)
137+
- [NonEmptyList](/docs/datatypes/NonEmptyList)
138+
- [Id](/docs/datatypes/Id)
139+
- [Function0](/docs/datatypes/Function0)
140+
141+
Additionally all instances of [`Monad`](/docs/typeclasses/monad) and their MTL variants implement the `Applicative` typeclass directly
142+
since they are all subtypes of `Applicative`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
---
2+
layout: docs
3+
title: Functor
4+
permalink: /docs/typeclasses/functor/
5+
---
6+
7+
## Functor
8+
9+
The `Functor` typeclass abstracts the ability to `map` over the computational context of a type constructor.
10+
Examples of type constructors that can implement instances of the Functor typeclass include `Option`, `NonEmptyList`,
11+
`List` and many other datatypes that include a `map` function with the shape `fun F<B>.map(f: (A) -> B): F<B>` where `F`
12+
refers to `Option`, `List` or any other type constructor whose contents can be transformed.
13+
14+
### Example
15+
16+
Often times we find ourselves in situations where we need to transform the contents of some datatype. `Functor#map` allows
17+
us to safely compute over values under the assumption that they'll be there returning the transformation encapsulated in the same context.
18+
19+
Consider both `Option` and `Try`:
20+
21+
`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.
22+
23+
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.
24+
25+
Both `Try` and `Option` are example datatypes that can be computed over transforming their inner results.
26+
27+
```kotlin:ank
28+
import kategory.*
29+
30+
Try { "1".toInt() }.map { it * 2 }
31+
Option(1).map { it * 2 }
32+
```
33+
34+
Both `Try` and `Option` include ready to use `Functor` instances:
35+
36+
```kotlin:ank
37+
val optionFunctor = Option.functor()
38+
```
39+
40+
```kotlin:ank
41+
val tryFunctor = Try.functor()
42+
```
43+
44+
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
45+
46+
```kotlin:ank
47+
Try { "x".toInt() }.map { it * 2 }
48+
none<Int>().map { it * 2 }
49+
```
50+
51+
Kategory allows abstract polymorphic code that operates over the evidence of having an instance of a typeclass available.
52+
This enables programs that are not coupled to specific datatype implementations.
53+
The technique demonstrated below to write polymorphic code is available for all other [Typeclasses](/docs/typeclasses) beside `Functor`.
54+
55+
```kotlin:ank
56+
inline fun <reified F> multiplyBy2(fa: HK<F, Int>, FT: Functor<F> = functor()): HK<F, Int> =
57+
FT.map(fa, { it * 2 })
58+
59+
multiplyBy2<OptionHK>(Option(1)) // Option(1)
60+
multiplyBy2<TryHK>(Try { 1 })
61+
```
62+
63+
In the example above we've defined a function that can operate over any data type for which a `Functor` instance is available.
64+
And then we applied `multiplyBy2` to two different datatypes for which Functor instances exist.
65+
This technique applied to other Typeclasses allows users to describe entire programs in terms of behaviors typeclasses removing
66+
dependencies to concrete data types and how they operate.
67+
68+
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)
69+
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)).
70+
71+
Entire libraries and applications can be written without enforcing consumers to use the lib author provided datatypes but letting
72+
users provide their own provided there is typeclass instances for their datatypes.
73+
74+
### Main Combinators
75+
76+
#### map
77+
78+
Transforms the inner contents
79+
80+
`fun <A, B> map(fa: HK<F, A>, f: (A) -> B): HK<F, B>`
81+
82+
```kotlin:ank
83+
optionFunctor.map(Option(1), { it + 1 })
84+
```
85+
86+
#### lift
87+
88+
Lift a function to the Functor context so it can be applied over values of the implementing datatype
89+
90+
`fun <A, B> lift(f: (A) -> B): (HK<F, A>) -> HK<F, B>`
91+
92+
```kotlin:ank
93+
val lifted = optionFunctor.lift({ n: Int -> n + 1 })
94+
lifted(Option(1))
95+
```
96+
97+
#### Other combinators
98+
99+
For a full list of other useful combinators available in `Functor` see the [`KDoc](/kdocs/typeclasses/functor)
100+
101+
### Syntax
102+
103+
#### HK<F, A>#map
104+
105+
Maps over any higher kinded type constructor for which a functor instance is found
106+
107+
```kotlin:ank
108+
Try { 1 }.map({ it + 1 })
109+
```
110+
111+
#### ((A) -> B)#lift
112+
113+
Lift a function into the functor context
114+
115+
```kotlin:ank
116+
val f = { n: Int -> n + 1 }.lift<OptionHK, Int, Int>()
117+
f(Option(1))
118+
```
119+
120+
121+
### Laws
122+
123+
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.
124+
125+
#### Creating your own `Functor` instances
126+
127+
[Kategory already provides Functor instances for most common datatypes](#datatypes) both in Kategory and the Kotlin stdlib.
128+
Often times you may find the need to provide your own for unsupported datatypes.
129+
130+
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
131+
as demonstrated in the [example](#example) above.
132+
133+
See [Deriving and creating custom typeclass]
134+
135+
### Data types
136+
137+
Thw following datatypes in Kategory provide instances that adhere to the `Functor` typeclass.
138+
139+
- [Cofree](/docs/datatypes/cofree)
140+
- [Coproduct](/docs/datatypes/coproduct)
141+
- [Coyoneda](/docs/datatypes/coyoneda)
142+
- [Either](/docs/datatypes/either)
143+
- [EitherT](/docs/datatypes/eitherT)
144+
- [FreeApplicative](/docs/datatypes/FreeApplicative)
145+
- [Function1](/docs/datatypes/Function1)
146+
- [Ior](/docs/datatypes/Ior)
147+
- [Kleisli](/docs/datatypes/Kleisli)
148+
- [OptionT](/docs/datatypes/OptionT)
149+
- [StateT](/docs/datatypes/StateT)
150+
- [Validated](/docs/datatypes/Validated)
151+
- [WriterT](/docs/datatypes/WriterT)
152+
- [Yoneda](/docs/datatypes/Yoneda)
153+
- [Const](/docs/datatypes/Const)
154+
- [Try](/docs/datatypes/Try)
155+
- [Eval](/docs/datatypes/Eval)
156+
- [IO](/docs/datatypes/IO)
157+
- [NonEmptyList](/docs/datatypes/NonEmptyList)
158+
- [Id](/docs/datatypes/Id)
159+
- [Function0](/docs/datatypes/Function0)
160+
161+
Additionally all instances of [`Applicative`](/docs/typeclasses/applicative), [`Monad`](/docs/typeclasses/monad) and their MTL variants implement the `Functor` typeclass directly
162+
since they are all subtypes of `Functor`

kategory-docs/gradle.properties

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Maven publishing configuration
2+
POM_NAME=Kategory-Docs
3+
POM_ARTIFACT_ID=kategory-docs
4+
POM_PACKAGING=jar

kategory/src/main/kotlin/kategory/typeclasses/Functor.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ interface Functor<F> : Typeclass {
1717

1818
fun <A, B> tupleLeft(fa: HK<F, A>, b: B): HK<F, Tuple2<B, A>> = map(fa, { a -> Tuple2(b, a) })
1919

20-
fun <A, B> tupleRigth(fa: HK<F, A>, b: B): HK<F, Tuple2<A, B>> = map(fa, { a -> Tuple2(a, b) })
20+
fun <A, B> tupleRight(fa: HK<F, A>, b: B): HK<F, Tuple2<A, B>> = map(fa, { a -> Tuple2(a, b) })
21+
2122
}
2223

2324
fun <F, B, A : B> Functor<F>.widen(fa: HK<F, A>): HK<F, B> = fa as HK<F, B>

0 commit comments

Comments
 (0)