Skip to content
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 18 commits into from
Aug 31, 2017
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added kategory-docs/README.md
Empty file.
15 changes: 15 additions & 0 deletions kategory-docs/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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"
}

sourceCompatibility = javaVersion
targetCompatibility = javaVersion

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')

compileKotlin.kotlinOptions.freeCompilerArgs += ["-Xskip-runtime-version-check"]
Empty file.
4 changes: 4 additions & 0 deletions kategory-docs/gradle.properties
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
Empty file.
138 changes: 138 additions & 0 deletions kategory-docs/typeclasses/applicative/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
---
layout: docs
title: Applicative
permalink: /docs/typeclasses/applicative/
---

## Functor

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 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
Copy link
Member

@JorgeCastilloPrz JorgeCastilloPrz Aug 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so is ank parsing this snippets and compiling them to fail CI if docs snippets are not compiling for any reason ?

And what about the docs autogeneration ? Can't see anything related on this PR. Don't we still have that ready ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes ank does all of that , the generated docs will be in markdown format which jekyll's support so you can push them to ghpages or anywhere else.

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.tupled(profileService(), phoneService(), addressService())
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.map(profileService(), phoneService(), addressService(), { name, phone, address ->
Profile(name, phone, address)
})
```

### 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.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<TryHK, Int> // Try.Success(1)
```

#### HK<F, A>#ap

Apply a function inside the type constructor's context

```kotlin:ank
Option(1).ap(Option({ n: Int -> n + 1 })) // Option(2)
```

#### HK<F, A>#map2

Map 2 values inside the type constructor context and apply a function to their cartesian product

```kotlin:ank
Option(1).map2(Option("x"), { z: Tuple2<Int, String> -> z.a + z.b }) // Option("1x")
```

#### 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(1).map2(Eval { Option("x") }, { z: Tuple2<Int, String> -> z.a + z.b }).value() // Option("1x")
```


### Laws

Kategory provides [`ApplicativeLaws`](/docs/typeclasses/laws#applicativelaws) 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo The


- [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`
151 changes: 151 additions & 0 deletions kategory-docs/typeclasses/functor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
---
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
Try { "1".toInt() }.map { it * 2 }
Option(1).map { it * 2 }
```

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
val optionFunctor = functor<OptionHK>()
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({ it + 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) 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`
3 changes: 2 additions & 1 deletion kategory/src/main/kotlin/kategory/typeclasses/Functor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ interface Functor<F> : Typeclass {

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

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

}

fun <F, B, A : B> Functor<F>.widen(fa: HK<F, A>): HK<F, B> = fa as HK<F, B>
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/data/OptionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class OptionTest : UnitSpec() {

"Option.functor.tupleRight should return a tuple the current value and the value passed" {
forAll { a: Int ->
Option.tupleRigth(Option(a), a + 1) == Some(Tuple2(a, a + 1))
Option.tupleRight(Option(a), a + 1) == Some(Tuple2(a, a + 1))
}
}

Expand Down
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
* limitations under the License.
*/

include ':kategory', ':kategory-test', ':kategory-effects'
include ':kategory', ':kategory-test', ':kategory-effects', ':kategory-docs'
rootProject.name = 'kategory-parent'