From 94ded8da580361666a37436fa757817ad60a6d38 Mon Sep 17 00:00:00 2001 From: Raul Raja Date: Sun, 13 Aug 2017 20:56:47 +0200 Subject: [PATCH 01/11] `kategory-docs` module and proposed layout for typeclasses and datatypes with `Functor` as example --- kategory-docs/README.md | 0 kategory-docs/build.gradle | 15 ++ kategory-docs/datatypes/README.md | 0 kategory-docs/typeclasses/README.md | 0 kategory-docs/typeclasses/functor/README.md | 152 ++++++++++++++++++ .../kotlin/kategory/typeclasses/Functor.kt | 3 +- .../test/kotlin/kategory/data/OptionTest.kt | 2 +- settings.gradle | 2 +- 8 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 kategory-docs/README.md create mode 100644 kategory-docs/build.gradle create mode 100644 kategory-docs/datatypes/README.md create mode 100644 kategory-docs/typeclasses/README.md create mode 100644 kategory-docs/typeclasses/functor/README.md diff --git a/kategory-docs/README.md b/kategory-docs/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/kategory-docs/build.gradle b/kategory-docs/build.gradle new file mode 100644 index 00000000000..0fc425ddc0a --- /dev/null +++ b/kategory-docs/build.gradle @@ -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"] \ No newline at end of file diff --git a/kategory-docs/datatypes/README.md b/kategory-docs/datatypes/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/kategory-docs/typeclasses/README.md b/kategory-docs/typeclasses/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/kategory-docs/typeclasses/functor/README.md b/kategory-docs/typeclasses/functor/README.md new file mode 100644 index 00000000000..4c4e07d9b76 --- /dev/null +++ b/kategory-docs/typeclasses/functor/README.md @@ -0,0 +1,152 @@ +--- +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.map(f: (A) -> B): F` 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` 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` 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.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 multiplyBy2(fa: HK, FT: Functor = functor()): HK = + FT.map(fa, { it * 2 }) + +multiplyBy2(Option(1)) // Option(1) +multiplyBy2(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 map(fa: HK, f: (A) -> B): HK` + +```kotlin:ank +val optionFunctor = functor() +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 lift(f: (A) -> B): (HK) -> HK` + +```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#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() +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. +Functors must obey the following laws: + +#### 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` \ No newline at end of file diff --git a/kategory/src/main/kotlin/kategory/typeclasses/Functor.kt b/kategory/src/main/kotlin/kategory/typeclasses/Functor.kt index 9fa3666c0d7..7cc4102fcea 100644 --- a/kategory/src/main/kotlin/kategory/typeclasses/Functor.kt +++ b/kategory/src/main/kotlin/kategory/typeclasses/Functor.kt @@ -17,7 +17,8 @@ interface Functor : Typeclass { fun tupleLeft(fa: HK, b: B): HK> = map(fa, { a -> Tuple2(b, a) }) - fun tupleRigth(fa: HK, b: B): HK> = map(fa, { a -> Tuple2(a, b) }) + fun tupleRight(fa: HK, b: B): HK> = map(fa, { a -> Tuple2(a, b) }) + } fun Functor.widen(fa: HK): HK = fa as HK diff --git a/kategory/src/test/kotlin/kategory/data/OptionTest.kt b/kategory/src/test/kotlin/kategory/data/OptionTest.kt index 1409e85a98e..7a874e3e292 100644 --- a/kategory/src/test/kotlin/kategory/data/OptionTest.kt +++ b/kategory/src/test/kotlin/kategory/data/OptionTest.kt @@ -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)) } } diff --git a/settings.gradle b/settings.gradle index 2a05048960c..ea63b0ae144 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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' From be2586404722fef1af55e2f0ce4066ae4c2bbfff Mon Sep 17 00:00:00 2001 From: Raul Raja Date: Sun, 13 Aug 2017 21:06:26 +0200 Subject: [PATCH 02/11] Add gradle.properties to docs module --- kategory-docs/gradle.properties | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 kategory-docs/gradle.properties diff --git a/kategory-docs/gradle.properties b/kategory-docs/gradle.properties new file mode 100644 index 00000000000..bfef807f4e2 --- /dev/null +++ b/kategory-docs/gradle.properties @@ -0,0 +1,4 @@ +# Maven publishing configuration +POM_NAME=Kategory-Docs +POM_ARTIFACT_ID=kategory-docs +POM_PACKAGING=jar From 68a65731d6c12b17ab68f134b7a264fa45d5e29c Mon Sep 17 00:00:00 2001 From: Raul Raja Date: Mon, 14 Aug 2017 23:14:51 +0200 Subject: [PATCH 03/11] Applicative docs --- .../typeclasses/applicative/README.md | 138 ++++++++++++++++++ kategory-docs/typeclasses/functor/README.md | 1 - 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 kategory-docs/typeclasses/applicative/README.md diff --git a/kategory-docs/typeclasses/applicative/README.md b/kategory-docs/typeclasses/applicative/README.md new file mode 100644 index 00000000000..0fcd5325556 --- /dev/null +++ b/kategory-docs/typeclasses/applicative/README.md @@ -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 +fun profileService(): Option = Option("Alfredo Lambda") +fun phoneService(): Option = Option(55555555) +fun addressService(): Option> = 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) + +val r: Option>> = 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 pure(a: A): HK` + +```kotlin:ank +Option.pure(1) // Option(1) +``` + +#### ap + +Apply a function inside the type constructor's context + +`fun ap(fa: HK, ff: HK B>): HK` + +```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#pure + +Lift a value into the computational context of a type constructor + +```kotlin:ank +1.pure // Try.Success(1) +``` + +#### HK#ap + +Apply a function inside the type constructor's context + +```kotlin:ank +Option(1).ap(Option({ n: Int -> n + 1 })) // Option(2) +``` + +#### HK#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 -> z.a + z.b }) // Option("1x") +``` + +#### HK#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 -> 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. + +- [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` \ No newline at end of file diff --git a/kategory-docs/typeclasses/functor/README.md b/kategory-docs/typeclasses/functor/README.md index 4c4e07d9b76..7da4180aec7 100644 --- a/kategory-docs/typeclasses/functor/README.md +++ b/kategory-docs/typeclasses/functor/README.md @@ -110,7 +110,6 @@ 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. -Functors must obey the following laws: #### Creating your own `Functor` instances From 857ea210233e8eb1f2d8b5964aa0c0dbf4a53433 Mon Sep 17 00:00:00 2001 From: Guardiola31337 Date: Mon, 21 Aug 2017 11:15:13 +0200 Subject: [PATCH 04/11] add ank --- build.gradle | 5 +++++ kategory-docs/build.gradle | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/build.gradle b/build.gradle index 3830b28729c..f2b68284db0 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ buildscript { kotlinVersion = '1.2-M2' kotlinxCoroutinesVersion = '0.17' kategoryAnnotationsVersion = '0.3.6' + kotlinxCollectionsImmutableVersion = '0.1' } repositories { @@ -37,6 +38,7 @@ buildscript { classpath "com.github.ben-manes:gradle-versions-plugin:$gradleVersionsPluginVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' + classpath group: 'io.kategory', name: 'ank-gradle-plugin', version: '0.1.2' } } @@ -49,6 +51,9 @@ subprojects { project -> jcenter() maven { url 'https://kotlin.bintray.com/kotlinx' } maven { url "http://dl.bintray.com/kotlin/kotlin-dev" } + maven { url "http://dl.bintray.com/kategory/maven" } + maven { url "https://dl.bintray.com/jetbrains/markdown/" } + maven { url 'https://jitpack.io' } } apply plugin: 'kotlin' diff --git a/kategory-docs/build.gradle b/kategory-docs/build.gradle index 0fc425ddc0a..eccf54a3c85 100644 --- a/kategory-docs/build.gradle +++ b/kategory-docs/build.gradle @@ -1,3 +1,5 @@ +apply plugin: 'ank-gradle-plugin' + dependencies { compile project(':kategory') compile project(':kategory-effects') @@ -7,6 +9,13 @@ dependencies { compile "io.kotlintest:kotlintest:$kotlinTestVersion" } +ank { + classpath = sourceSets.main.runtimeClasspath + arguments = ["${projectDir}/typeclasses/applicative", + "${projectDir}/site", + "-classpath", "${sourceSets.main.runtimeClasspath.join(":")}"] +} + sourceCompatibility = javaVersion targetCompatibility = javaVersion From ee33a2d88aef0d4b54ccc1857084b4d5fc62f646 Mon Sep 17 00:00:00 2001 From: Raul Raja Date: Tue, 22 Aug 2017 13:09:56 +0200 Subject: [PATCH 05/11] reorg files --- kategory-docs/build.gradle | 4 ++-- kategory-docs/{ => docs}/datatypes/README.md | 0 kategory-docs/{ => docs}/typeclasses/README.md | 0 kategory-docs/{ => docs}/typeclasses/applicative/README.md | 2 ++ kategory-docs/{ => docs}/typeclasses/functor/README.md | 2 ++ 5 files changed, 6 insertions(+), 2 deletions(-) rename kategory-docs/{ => docs}/datatypes/README.md (100%) rename kategory-docs/{ => docs}/typeclasses/README.md (100%) rename kategory-docs/{ => docs}/typeclasses/applicative/README.md (99%) rename kategory-docs/{ => docs}/typeclasses/functor/README.md (99%) diff --git a/kategory-docs/build.gradle b/kategory-docs/build.gradle index eccf54a3c85..6609950af7c 100644 --- a/kategory-docs/build.gradle +++ b/kategory-docs/build.gradle @@ -11,8 +11,8 @@ dependencies { ank { classpath = sourceSets.main.runtimeClasspath - arguments = ["${projectDir}/typeclasses/applicative", - "${projectDir}/site", + arguments = ["${projectDir}/docs", + "${projectDir}/build/site", "-classpath", "${sourceSets.main.runtimeClasspath.join(":")}"] } diff --git a/kategory-docs/datatypes/README.md b/kategory-docs/docs/datatypes/README.md similarity index 100% rename from kategory-docs/datatypes/README.md rename to kategory-docs/docs/datatypes/README.md diff --git a/kategory-docs/typeclasses/README.md b/kategory-docs/docs/typeclasses/README.md similarity index 100% rename from kategory-docs/typeclasses/README.md rename to kategory-docs/docs/typeclasses/README.md diff --git a/kategory-docs/typeclasses/applicative/README.md b/kategory-docs/docs/typeclasses/applicative/README.md similarity index 99% rename from kategory-docs/typeclasses/applicative/README.md rename to kategory-docs/docs/typeclasses/applicative/README.md index 0fcd5325556..336c2754d20 100644 --- a/kategory-docs/typeclasses/applicative/README.md +++ b/kategory-docs/docs/typeclasses/applicative/README.md @@ -17,6 +17,8 @@ Often times we find ourselves in situations where we need to compute multiple in 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 = Option("Alfredo Lambda") fun phoneService(): Option = Option(55555555) fun addressService(): Option> = Option(listOf("1 Main Street", "11130", "NYC")) diff --git a/kategory-docs/typeclasses/functor/README.md b/kategory-docs/docs/typeclasses/functor/README.md similarity index 99% rename from kategory-docs/typeclasses/functor/README.md rename to kategory-docs/docs/typeclasses/functor/README.md index 7da4180aec7..f879ce1ab82 100644 --- a/kategory-docs/typeclasses/functor/README.md +++ b/kategory-docs/docs/typeclasses/functor/README.md @@ -25,6 +25,8 @@ In a similar fashion `Try` may have two possible cases `Success(a: A)` for co 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 } ``` From 2cb24b4e52861d294e420d0ee8cfb273955667e5 Mon Sep 17 00:00:00 2001 From: Guardiola31337 Date: Sun, 27 Aug 2017 18:41:34 +0200 Subject: [PATCH 06/11] bump ank-gradle-plugin version to 0.1.3, kotlinx coroutines version to 0.18 and kategory annotations version to 0.3.7 --- build.gradle | 6 +++--- kategory-docs/build.gradle | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index c463ac4b8f0..abc77f7bcd5 100644 --- a/build.gradle +++ b/build.gradle @@ -24,8 +24,8 @@ buildscript { javaVersion = JavaVersion.VERSION_1_7 kotlinTestVersion = '2.0.5' kotlinVersion = '1.1.4' - kotlinxCoroutinesVersion = '0.17' - kategoryAnnotationsVersion = '0.3.6' + kotlinxCoroutinesVersion = '0.18' + kategoryAnnotationsVersion = '0.3.7' kotlinxCollectionsImmutableVersion = '0.1' } @@ -38,7 +38,7 @@ buildscript { classpath "com.github.ben-manes:gradle-versions-plugin:$gradleVersionsPluginVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' - classpath group: 'io.kategory', name: 'ank-gradle-plugin', version: '0.1.2' + classpath group: 'io.kategory', name: 'ank-gradle-plugin', version: '0.1.3' } } diff --git a/kategory-docs/build.gradle b/kategory-docs/build.gradle index 6609950af7c..fb265df9fe8 100644 --- a/kategory-docs/build.gradle +++ b/kategory-docs/build.gradle @@ -10,10 +10,9 @@ dependencies { } ank { + source = file("${projectDir}/docs") + target = file("${projectDir}/build/site") classpath = sourceSets.main.runtimeClasspath - arguments = ["${projectDir}/docs", - "${projectDir}/build/site", - "-classpath", "${sourceSets.main.runtimeClasspath.join(":")}"] } sourceCompatibility = javaVersion From bf6c09cabdb0ae0c5140164791d1b4e822a11352 Mon Sep 17 00:00:00 2001 From: Raul Raja Date: Mon, 28 Aug 2017 00:25:06 +0000 Subject: [PATCH 07/11] fixes docs code snippets and excludes kategory since it is brought in by ank for the time being. --- build.gradle | 6 +++++- kategory-docs/build.gradle | 9 ++++----- .../docs/typeclasses/applicative/README.md | 18 +++++++++--------- .../docs/typeclasses/functor/README.md | 17 +++++++++++++---- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/build.gradle b/build.gradle index e0ad688cc88..850d2be332e 100644 --- a/build.gradle +++ b/build.gradle @@ -31,14 +31,18 @@ buildscript { repositories { jcenter() + maven { url 'https://jitpack.io' } maven { url "http://dl.bintray.com/kotlin/kotlin-dev" } + maven { url "https://dl.bintray.com/jetbrains/markdown/" } } dependencies { classpath "com.github.ben-manes:gradle-versions-plugin:$gradleVersionsPluginVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' - classpath group: 'io.kategory', name: 'ank-gradle-plugin', version: '0.1.2' + classpath('com.github.kategory:ank:master-SNAPSHOT') { + exclude group: 'com.github.kategory' + } } } diff --git a/kategory-docs/build.gradle b/kategory-docs/build.gradle index 6609950af7c..936b22f60a3 100644 --- a/kategory-docs/build.gradle +++ b/kategory-docs/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'ank-gradle-plugin' dependencies { - compile project(':kategory') - compile project(':kategory-effects') + //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" @@ -10,10 +10,9 @@ dependencies { } ank { + source = file("${projectDir}/docs") + target = file("${projectDir}/build/site") classpath = sourceSets.main.runtimeClasspath - arguments = ["${projectDir}/docs", - "${projectDir}/build/site", - "-classpath", "${sourceSets.main.runtimeClasspath.join(":")}"] } sourceCompatibility = javaVersion diff --git a/kategory-docs/docs/typeclasses/applicative/README.md b/kategory-docs/docs/typeclasses/applicative/README.md index 336c2754d20..33486dd067f 100644 --- a/kategory-docs/docs/typeclasses/applicative/README.md +++ b/kategory-docs/docs/typeclasses/applicative/README.md @@ -4,7 +4,7 @@ title: Applicative permalink: /docs/typeclasses/applicative/ --- -## Functor +## 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`, @@ -31,15 +31,15 @@ Kategory features an [Applicative Builder](/docs/patterns/applicativebuilder) th ```kotlin:ank data class Profile(val name: String, val phone: Int, val address: List) -val r: Option>> = Option.tupled(profileService(), phoneService(), addressService()) +val r: Option>> = 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.map(profileService(), phoneService(), addressService(), { name, phone, address -> - Profile(name, phone, address) +Option.applicative().map(profileService(), phoneService(), addressService(), { (name, phone, addresses) -> + Profile(name, phone, addresses) }) ``` @@ -62,7 +62,7 @@ Apply a function inside the type constructor's context `fun ap(fa: HK, ff: HK B>): HK` ```kotlin:ank -Option.ap(Option(1), Option({ n: Int -> n + 1 })) // Option(2) +Option.applicative().ap(Option(1), Option({ n: Int -> n + 1 })) // Option(2) ``` #### Other combinators @@ -76,7 +76,7 @@ For a full list of other useful combinators available in `Applicative` see the [ Lift a value into the computational context of a type constructor ```kotlin:ank -1.pure // Try.Success(1) +1.pure() ``` #### HK#ap @@ -84,7 +84,7 @@ Lift a value into the computational context of a type constructor Apply a function inside the type constructor's context ```kotlin:ank -Option(1).ap(Option({ n: Int -> n + 1 })) // Option(2) +Option(1).ap(Option({ n: Int -> n + 1 })) ``` #### HK#map2 @@ -92,7 +92,7 @@ Option(1).ap(Option({ n: Int -> n + 1 })) // Option(2) 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 -> z.a + z.b }) // Option("1x") +Option.applicative().map2(Option(1), Option("x"), { z: Tuple2 -> "${z.a}${z.b}" }) ``` #### HK#map2Eval @@ -101,7 +101,7 @@ Lazily map 2 values inside the type constructor context and apply a function to Computation happens when `.value()` is invoked. ```kotlin:ank -Option(1).map2(Eval { Option("x") }, { z: Tuple2 -> z.a + z.b }).value() // Option("1x") +Option.applicative().map2Eval(Option(1), Eval.later { Option("x") }, { z: Tuple2 -> "${z.a}${z.b}" }).value() ``` diff --git a/kategory-docs/docs/typeclasses/functor/README.md b/kategory-docs/docs/typeclasses/functor/README.md index f879ce1ab82..30690dc2d9e 100644 --- a/kategory-docs/docs/typeclasses/functor/README.md +++ b/kategory-docs/docs/typeclasses/functor/README.md @@ -31,11 +31,21 @@ 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.map { it * 2 } +none().map { it * 2 } ``` Kategory allows abstract polymorphic code that operates over the evidence of having an instance of a typeclass available. @@ -70,8 +80,7 @@ Transforms the inner contents `fun map(fa: HK, f: (A) -> B): HK` ```kotlin:ank -val optionFunctor = functor() -optionfunctor.map(Option(1), { it + 1 }) +optionFunctor.map(Option(1), { it + 1 }) ``` #### lift @@ -81,7 +90,7 @@ Lift a function to the Functor context so it can be applied over values of the i `fun lift(f: (A) -> B): (HK) -> HK` ```kotlin:ank -val lifted = optionFunctor.lift({ it + 1 }) +val lifted = optionFunctor.lift({ n: Int -> n + 1 }) lifted(Option(1)) ``` From 63767bba0ac658b650d2e8f560f79ad1aeaca485 Mon Sep 17 00:00:00 2001 From: Raul Raja Date: Mon, 28 Aug 2017 00:27:40 +0000 Subject: [PATCH 08/11] Build docs on CI --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 044f1a030e5..957bfca1cb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ jdk: - oraclejdk8 script: - - ./gradlew clean build + - ./gradlew clean build :kategory-docs:runAnk - ./gradlew codeCoverageReport after_success: From ceb7ec60baa2c0e4a12f3b03e33486a53a376096 Mon Sep 17 00:00:00 2001 From: Guardiola31337 Date: Tue, 29 Aug 2017 00:01:02 +0200 Subject: [PATCH 09/11] bump ank-gradle-plugin dependency to 0.1.4 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 118542a3e38..fa936f3bbac 100644 --- a/build.gradle +++ b/build.gradle @@ -40,8 +40,8 @@ buildscript { classpath "com.github.ben-manes:gradle-versions-plugin:$gradleVersionsPluginVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' - classpath('com.github.kategory:ank:master-SNAPSHOT') { - exclude group: 'com.github.kategory' + classpath('io.kategory:ank-gradle-plugin:0.1.4') { + exclude group: 'io.kategory' } } } From 4434e8788e5792288067bab7aaaad421ea511f0d Mon Sep 17 00:00:00 2001 From: Raul Raja Date: Thu, 31 Aug 2017 11:51:52 +0200 Subject: [PATCH 10/11] Add note on `Applicative` addressing https://github.com/kategory/kategory/pull/191#issuecomment-325211996 --- kategory-docs/docs/typeclasses/applicative/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kategory-docs/docs/typeclasses/applicative/README.md b/kategory-docs/docs/typeclasses/applicative/README.md index 33486dd067f..888f69321a1 100644 --- a/kategory-docs/docs/typeclasses/applicative/README.md +++ b/kategory-docs/docs/typeclasses/applicative/README.md @@ -10,6 +10,8 @@ The `Applicative` typeclass abstracts the ability to lift values and apply funct 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. From 3886388d71b3285bc7e2bf64d5c85438ccd37380 Mon Sep 17 00:00:00 2001 From: Raul Raja Date: Thu, 31 Aug 2017 11:54:00 +0200 Subject: [PATCH 11/11] add clarification that laws are actual test cases --- kategory-docs/docs/typeclasses/applicative/README.md | 2 +- kategory-docs/docs/typeclasses/functor/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kategory-docs/docs/typeclasses/applicative/README.md b/kategory-docs/docs/typeclasses/applicative/README.md index 888f69321a1..1e11c430674 100644 --- a/kategory-docs/docs/typeclasses/applicative/README.md +++ b/kategory-docs/docs/typeclasses/applicative/README.md @@ -109,7 +109,7 @@ Option.applicative().map2Eval(Option(1), Eval.later { Option("x") }, { z: Tuple2 ### Laws -Kategory provides [`ApplicativeLaws`](/docs/typeclasses/laws#applicativelaws) for internal verification of lawful instances and third party apps creating their own Applicative instances. +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 diff --git a/kategory-docs/docs/typeclasses/functor/README.md b/kategory-docs/docs/typeclasses/functor/README.md index 30690dc2d9e..a31a2667d42 100644 --- a/kategory-docs/docs/typeclasses/functor/README.md +++ b/kategory-docs/docs/typeclasses/functor/README.md @@ -120,7 +120,7 @@ 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. +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