From d371e3526c58677e949ae13001f71913d1e195d5 Mon Sep 17 00:00:00 2001 From: abergfeld Date: Fri, 24 Aug 2018 12:18:36 -0500 Subject: [PATCH 1/8] Add docs around Coproduct --- .../docs/docs/generic/coproduct/README.md | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md diff --git a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md new file mode 100644 index 00000000000..710b8eeac7a --- /dev/null +++ b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md @@ -0,0 +1,140 @@ +--- +layout: docs +title: Generic +permalink: /docs/generic/coproduct/ +--- + +## Arrow Generic + +{:.beginner} +beginner + +`arrow-generic` provides meta programming facilities over Product types like data classes, tuples, and heterogeneous lists; and Coproduct types like sealed classes. + +### Install + +```groovy +compile 'io.arrow-kt:arrow-generic:$arrow_version' +``` + +### Features + +#### Coproduct + +Coproducts represent a container in which only one of the specified set of types exist. It's very similar in `Either` in that respect. `Either` supports one of two values, an `Either` has to contain an instance of `A` or `B`. We can extrapolate that concept to `N` number of types. So a `Coproduct5` has to contain an instance of `A`, `B`, `C`, `D`, or `E`. For example, perhaps there's a search function for a Car Dealer app that can show `Dealership`s, `Car`s and `SalesPerson`s, we could model that list of results as `List`. The result would contain a list of heterogeneous elements but each element is one of `Dealership`, `Car` or `SalesPerson` and our UI can render the list elements based on those types. + +Let's say we have an api. Our api operates under the following conditions: +- Every endpoint could tell the client, `ServerError`, `UserUnauthorized` or `OverRequestLimit`. +- For specific endpoints, we may have specific results to the context of what they do, for example, an endpoint for registering a `Car` for some service. This endpoint could return to us `CarAlreadyRegistered`, `StolenCar` or `SuccessfullyRegistered`. For fun, let's say the `SuccessfullyRegistered` response also contains a `Registration` object with some data. + +Because we have some common errors that every endpoint can return to us, we can define a sealed class of these and call them `CommonServerError` because they make sense to be sealed together for reusability. Likewise, we can logically group our specific errors into `RegistrationError` and we have `Registration` as our success type. + +With Coproducts, we're able to define a result for this api call as `Coproduct3`. We've been able to compose these results without having to write our own sealed class containing all the common errors for each endpoint. + +#### Extensions + +##### coproductOf + +So now that we've got our api response modeled, we need to be able to create an instance of `Coproduct3`. + +```kotlin:ank +coproductOf(ServerError) //Returns Coproduct3 +``` + +There are `coproductOf` constructor functions for each Coproduct. All we have to do is pass in our value and have the correct type parameters on it, the value must be a Type declared on the function call. + +If we pass in a value that doesn't correspond to any types on the Coproduct, it won't compile: +```kolint:ank +coproductOf(ServerError) //Doesn't compile +``` + +##### cop + +You might be saying "That's great and all but passing in values as parameters is so Java, I want something more Kotlin!". Well look no further, just like `Either`'s `left()` and `right()` extension methods, Coproducts can be created with an extension method on any type: + +```kotlin:ank +ServerError.cop() //Returns Coproduct3 +``` + +All we have to do is provide the type parameters we can make a Coproduct using the `cop` extension method. Just like `coproductOf`, if the Type of the value isn't in the Type parameters of the method call, it won't compile: + +```kotlin:ank +ServerError.cop() //Doesn't compile +``` + +##### fold + +Obviously, we're not just modeling errors for fun, we're going to handle them! All Coproducts have `fold` which allows us to condense the Coproduct down to a single type. For example, we could handle errors as such in a UI: + +```kotlin:ank +fun renderApiResult(apiResult: Coproduct3) = apiResult.fold( + { commonError -> + when (commonError) { + ServerError -> //Show error + UserUnauthorized -> //Log out user + OverRequestLimit -> //Show error + } + }, + { registrationError -> + when (registrationError) { + RegistrationAlreadyExists -> //Show that the car is already registered + CarStolen -> //Call the police! + } + }, + { registration -> + val registeredCar = registration.car + + //Render success! Yay! + } + ) +``` + +This example likely would return `Unit` from the `fold` as there's likely not a common type to be returned for showing various UI elements and this is likely all side effects, let's say our application was built for a command line and we just have to show a `String` for the result of the call (if only it was always that easy): + +```kotlin:ank + +fun renderApiResult(apiResult: Coproduct3): String = apiResult.fold( + { commonError -> + when (commonError) { + ServerError -> "Server error, try again later." + UserUnauthorized -> "Unauthorized!" + OverRequestLimit -> "Too many api requests, try again later." + } + }, + { registrationError -> + when (registrationError) { + RegistrationAlreadyExists -> "Car already registered." + CarStolen -> "Car reported stolen!" + } + }, + { registration -> + "Successfully Registered: $registration.car" + } + ) +``` + +Here we're able to return the result of the `fold` and we're forced to handle all cases! Neat! + +##### select + +Let's say we also want to store the `Registration` object into our database when we successfully register a car, because we're a good offline first application like that. We don't really want to have to `fold` over every single case just to handle something for the `Registration`, this is where `select` comes to the rescue! We're able to take a Coproduct and `select` the type we care about from it. `select` returns an `Option`, if the value of the Coproduct was for the type you're trying to `select`, you'll get `Some`, if it was not the type used with `select`, you'll get `None`. + +```kotlin:ank +fun handleApiResult( + database: Database, + apiResult: Coproduct3 +): Unit { + apiResult.select() + .fold( + {}, //Wasn't Registration, nothing to do here + { database.insertRegistration(it) } + ) +} +``` + +`select` can only be called with a type that exists on the Coproduct, if the type doesn't exist, it won't compile: +```kotlin:ank +fun handleApiResult(apiResult: Coproduct3): Unit { + apiResult.select() //Doesn't compile +} +``` From 2e428639817551fbdc01f824c0e61a660774e336 Mon Sep 17 00:00:00 2001 From: abergfeld Date: Fri, 24 Aug 2018 12:29:51 -0500 Subject: [PATCH 2/8] Clean up phrasing / type casing --- .../docs/arrow-docs/docs/docs/generic/coproduct/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md index 710b8eeac7a..6cdc08a29ea 100644 --- a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md +++ b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md @@ -41,7 +41,7 @@ So now that we've got our api response modeled, we need to be able to create an coproductOf(ServerError) //Returns Coproduct3 ``` -There are `coproductOf` constructor functions for each Coproduct. All we have to do is pass in our value and have the correct type parameters on it, the value must be a Type declared on the function call. +There are `coproductOf` constructor functions for each Coproduct. All we have to do is pass in our value and have the correct type parameters on it, the value must be a type declared on the function call. If we pass in a value that doesn't correspond to any types on the Coproduct, it won't compile: ```kolint:ank @@ -56,7 +56,7 @@ You might be saying "That's great and all but passing in values as parameters is ServerError.cop() //Returns Coproduct3 ``` -All we have to do is provide the type parameters we can make a Coproduct using the `cop` extension method. Just like `coproductOf`, if the Type of the value isn't in the Type parameters of the method call, it won't compile: +All we have to do is provide the type parameters and we can make a Coproduct using the `cop` extension method. Just like `coproductOf`, if the type of the value isn't in the type parameters of the method call, it won't compile: ```kotlin:ank ServerError.cop() //Doesn't compile @@ -89,7 +89,7 @@ fun renderApiResult(apiResult: Coproduct3` comes to the rescue! We're able to take a Coproduct and `select` the type we care about from it. `select` returns an `Option`, if the value of the Coproduct was for the type you're trying to `select`, you'll get `Some`, if it was not the type used with `select`, you'll get `None`. +Let's say we also want to store the `Registration` object into our database when we successfully register a car. We don't really want to have to `fold` over every single case just to handle something for the `Registration`, this is where `select` comes to the rescue! We're able to take a Coproduct and `select` the type we care about from it. `select` returns an `Option`, if the value of the Coproduct was for the type you're trying to `select`, you'll get `Some`, if it was not the type used with `select`, you'll get `None`. ```kotlin:ank fun handleApiResult( From cf53d9ce46edb9146dcc2056e84bd462695be5ff Mon Sep 17 00:00:00 2001 From: abergfeld Date: Fri, 24 Aug 2018 14:45:37 -0500 Subject: [PATCH 3/8] It's compiling docs --- .../docs/docs/generic/coproduct/README.md | 61 +++++++++++++------ .../src/main/kotlin/GenericHelper.kt | 21 +++++++ 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md index 6cdc08a29ea..d8acb62294b 100644 --- a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md +++ b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md @@ -38,14 +38,17 @@ With Coproducts, we're able to define a result for this api call as `Coproduct3< So now that we've got our api response modeled, we need to be able to create an instance of `Coproduct3`. ```kotlin:ank -coproductOf(ServerError) //Returns Coproduct3 +import arrow.generic.* +import arrow.generic.coproduct3.coproductOf + +val apiResult = coproductOf(ServerError) //Returns Coproduct3 ``` There are `coproductOf` constructor functions for each Coproduct. All we have to do is pass in our value and have the correct type parameters on it, the value must be a type declared on the function call. If we pass in a value that doesn't correspond to any types on the Coproduct, it won't compile: ```kolint:ank -coproductOf(ServerError) //Doesn't compile +//val apiResult = coproductOf(ServerError) ``` ##### cop @@ -53,13 +56,16 @@ coproductOf(ServerError) //Doesn't comp You might be saying "That's great and all but passing in values as parameters is so Java, I want something more Kotlin!". Well look no further, just like `Either`'s `left()` and `right()` extension methods, Coproducts can be created with an extension method on any type: ```kotlin:ank -ServerError.cop() //Returns Coproduct3 +import arrow.generic.* +import arrow.generic.coproduct3.cop + +val apiResult = ServerError.cop() //Returns Coproduct3 ``` All we have to do is provide the type parameters and we can make a Coproduct using the `cop` extension method. Just like `coproductOf`, if the type of the value isn't in the type parameters of the method call, it won't compile: ```kotlin:ank -ServerError.cop() //Doesn't compile +//val apiResult = ServerError.cop() ``` ##### fold @@ -67,24 +73,34 @@ ServerError.cop() //Doesn't compile Obviously, we're not just modeling errors for fun, we're going to handle them! All Coproducts have `fold` which allows us to condense the Coproduct down to a single type. For example, we could handle errors as such in a UI: ```kotlin:ank +import arrow.generic.* +import arrow.generic.coproduct3.Coproduct3 +import arrow.generic.coproduct3.fold + +fun handleCommonError(commonError: CommonServerError) { +} + +fun showCarAlreadyRegistered() { +} + +fun callPolice() { +} + +fun showCarSuccessfullyRegistered(car: Car) { +} + fun renderApiResult(apiResult: Coproduct3) = apiResult.fold( - { commonError -> - when (commonError) { - ServerError -> //Show error - UserUnauthorized -> //Log out user - OverRequestLimit -> //Show error - } - }, + { commonError -> handleCommonError(commonError) }, { registrationError -> when (registrationError) { - RegistrationAlreadyExists -> //Show that the car is already registered - CarStolen -> //Call the police! + CarAlreadyRegistered -> showCarAlreadyRegistered() + StolenCar -> callPolice() } }, { registration -> val registeredCar = registration.car - //Render success! Yay! + showCarSuccessfullyRegistered(registeredCar) } ) ``` @@ -92,6 +108,9 @@ fun renderApiResult(apiResult: Coproduct3): String = apiResult.fold( { commonError -> @@ -103,8 +122,8 @@ fun renderApiResult(apiResult: Coproduct3 when (registrationError) { - RegistrationAlreadyExists -> "Car already registered." - CarStolen -> "Car reported stolen!" + CarAlreadyRegistered -> "Car already registered." + StolenCar -> "Car reported stolen!" } }, { registration -> @@ -120,6 +139,10 @@ Here we're able to return the result of the `fold` and we're forced to handle al Let's say we also want to store the `Registration` object into our database when we successfully register a car. We don't really want to have to `fold` over every single case just to handle something for the `Registration`, this is where `select` comes to the rescue! We're able to take a Coproduct and `select` the type we care about from it. `select` returns an `Option`, if the value of the Coproduct was for the type you're trying to `select`, you'll get `Some`, if it was not the type used with `select`, you'll get `None`. ```kotlin:ank +import arrow.generic.* +import arrow.generic.coproduct3.Coproduct3 +import arrow.generic.coproduct3.select + fun handleApiResult( database: Database, apiResult: Coproduct3 @@ -134,7 +157,11 @@ fun handleApiResult( `select` can only be called with a type that exists on the Coproduct, if the type doesn't exist, it won't compile: ```kotlin:ank +import arrow.generic.* +import arrow.generic.coproduct3.Coproduct3 +import arrow.generic.coproduct3.select + fun handleApiResult(apiResult: Coproduct3): Unit { - apiResult.select() //Doesn't compile + //apiResult.select() Doesn't compile } ``` diff --git a/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt b/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt index 0bfa7a24818..7f403f03d48 100644 --- a/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt +++ b/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt @@ -1,6 +1,10 @@ package arrow.generic import arrow.product +import arrow.generic.coproduct3.coproductOf +import arrow.generic.coproduct3.fold +import arrow.generic.coproduct3.select +import arrow.generic.coproduct3.Coproduct3 @product data class Account(val balance: Int, val available: Int) { @@ -15,4 +19,21 @@ data class Speed(val kmh: Int) { @product data class Car(val speed: Speed) { companion object +} + +sealed class CommonServerError +object ServerError : CommonServerError() +object UserUnauthorized : CommonServerError() +object OverRequestLimit : CommonServerError() + +sealed class RegistrationError +object CarAlreadyRegistered : RegistrationError() +object StolenCar : RegistrationError() + +data class SuccessfullyRegistered(val registration: Registration) + +data class Registration(val car: Car) + +interface Database { + fun insertRegistration(registration: Registration) } \ No newline at end of file From c9b406779d767d2733ae8eace22a941e4a37afe6 Mon Sep 17 00:00:00 2001 From: abergfeld Date: Fri, 24 Aug 2018 14:56:29 -0500 Subject: [PATCH 4/8] Add Coproducts under the generic menu --- modules/docs/arrow-docs/docs/_data/menu.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/docs/arrow-docs/docs/_data/menu.yml b/modules/docs/arrow-docs/docs/_data/menu.yml index 9d186fdb668..3c6e4a3a5ec 100644 --- a/modules/docs/arrow-docs/docs/_data/menu.yml +++ b/modules/docs/arrow-docs/docs/_data/menu.yml @@ -281,6 +281,9 @@ options: - title: Generic nested_options: + - title: Coproduct + url: /docs/generic/coproduct + - title: Product url: /docs/generic/product/ From 652cf61b3f27bd849ef02d6c926aff0865221df2 Mon Sep 17 00:00:00 2001 From: abergfeld Date: Mon, 27 Aug 2018 09:51:59 -0500 Subject: [PATCH 5/8] Remove unused imports --- modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt b/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt index 7f403f03d48..7f0683711e7 100644 --- a/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt +++ b/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt @@ -1,10 +1,6 @@ package arrow.generic import arrow.product -import arrow.generic.coproduct3.coproductOf -import arrow.generic.coproduct3.fold -import arrow.generic.coproduct3.select -import arrow.generic.coproduct3.Coproduct3 @product data class Account(val balance: Int, val available: Int) { From 1b7c7492c5d009d8bb122332192c303659d543a7 Mon Sep 17 00:00:00 2001 From: abergfeld Date: Tue, 28 Aug 2018 19:37:25 -0500 Subject: [PATCH 6/8] Update after comments --- .../docs/docs/generic/coproduct/README.md | 29 +++++++++++++++---- .../src/main/kotlin/GenericHelper.kt | 3 ++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md index d8acb62294b..029c34b1af8 100644 --- a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md +++ b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md @@ -9,7 +9,7 @@ permalink: /docs/generic/coproduct/ {:.beginner} beginner -`arrow-generic` provides meta programming facilities over Product types like data classes, tuples, and heterogeneous lists; and Coproduct types like sealed classes. +`arrow-generic` provides meta programming facilities over Product types (data classes, tuples, heterogeneous lists...). It also provides the Coproduct type, similar to sealed classes. ### Install @@ -21,7 +21,23 @@ compile 'io.arrow-kt:arrow-generic:$arrow_version' #### Coproduct -Coproducts represent a container in which only one of the specified set of types exist. It's very similar in `Either` in that respect. `Either` supports one of two values, an `Either` has to contain an instance of `A` or `B`. We can extrapolate that concept to `N` number of types. So a `Coproduct5` has to contain an instance of `A`, `B`, `C`, `D`, or `E`. For example, perhaps there's a search function for a Car Dealer app that can show `Dealership`s, `Car`s and `SalesPerson`s, we could model that list of results as `List`. The result would contain a list of heterogeneous elements but each element is one of `Dealership`, `Car` or `SalesPerson` and our UI can render the list elements based on those types. +Coproducts represent a sealed hierarchy of types where only one of the specified set of types exist every time. Conceptually, it's very similar to the stdlib `sealed` class, or to [Either]({{ '/docs/datatypes/either' | relative_url }}) if we move on to Arrow data types. [Either]({{ '/docs/datatypes/either' | relative_url }}) supports one of two values, an `Either` has to contain an instance of `A` or `B`. We can extrapolate that concept to `N` number of types. So a `Coproduct5` has to contain an instance of `A`, `B`, `C`, `D`, or `E`. For example, perhaps there's a search function for a Car Dealer app that can show `Dealership`s, `Car`s and `SalesPerson`s in a list, we could model that list of results as `List`. The result would contain a list of heterogeneous elements but each element is one of `Dealership`, `Car` or `SalesPerson` and our UI can render the list elements based on those types. + +```kotlin:ank +import arrow.generic.* +import arrow.generic.coproduct3.Coproduct3 +import arrow.generic.coproduct3.fold + +fun toDisplayValues(items: List>): List { + return items.map { + it.fold( + { "Car: Speed: ${it.speed.kmh}" }, + { "Dealership: ${it.location}" }, + { "Salesperson: ${it.name}"} + ) + } +} +``` Let's say we have an api. Our api operates under the following conditions: - Every endpoint could tell the client, `ServerError`, `UserUnauthorized` or `OverRequestLimit`. @@ -44,16 +60,17 @@ import arrow.generic.coproduct3.coproductOf val apiResult = coproductOf(ServerError) //Returns Coproduct3 ``` -There are `coproductOf` constructor functions for each Coproduct. All we have to do is pass in our value and have the correct type parameters on it, the value must be a type declared on the function call. +There are `coproductOf` constructor functions for each Coproduct regardless of the arity (number of types). All we have to do is pass in our value and have the correct type parameters on it, the value must be a type declared on the function call. If we pass in a value that doesn't correspond to any types on the Coproduct, it won't compile: + ```kolint:ank //val apiResult = coproductOf(ServerError) ``` ##### cop -You might be saying "That's great and all but passing in values as parameters is so Java, I want something more Kotlin!". Well look no further, just like `Either`'s `left()` and `right()` extension methods, Coproducts can be created with an extension method on any type: +You might be saying "That's great and all but passing in values as parameters is so Java, I want something more Kotlin!". Well look no further, just like [Either]({{ '/docs/datatypes/either' | relative_url }})'s `left()` and `right()` extension methods, Coproducts can be created with an extension method on any type: ```kotlin:ank import arrow.generic.* @@ -132,11 +149,11 @@ fun renderApiResult(apiResult: Coproduct3` comes to the rescue! ##### select -Let's say we also want to store the `Registration` object into our database when we successfully register a car. We don't really want to have to `fold` over every single case just to handle something for the `Registration`, this is where `select` comes to the rescue! We're able to take a Coproduct and `select` the type we care about from it. `select` returns an `Option`, if the value of the Coproduct was for the type you're trying to `select`, you'll get `Some`, if it was not the type used with `select`, you'll get `None`. +We're able to take a Coproduct and `select` the type we care about from it. `select` returns an `Option`, if the value of the Coproduct was for the type you're trying to `select`, you'll get `Some`, if it was not the type used with `select`, you'll get `None`. ```kotlin:ank import arrow.generic.* diff --git a/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt b/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt index 7f0683711e7..71893c0df39 100644 --- a/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt +++ b/modules/docs/arrow-docs/src/main/kotlin/GenericHelper.kt @@ -17,6 +17,9 @@ data class Car(val speed: Speed) { companion object } +data class Salesperson(val name: String) +data class Dealership(val location: String) + sealed class CommonServerError object ServerError : CommonServerError() object UserUnauthorized : CommonServerError() From 7384f3cf97424df3b970fd0d5ccc6f98bd97cfa8 Mon Sep 17 00:00:00 2001 From: abergfeld Date: Tue, 28 Aug 2018 19:47:10 -0500 Subject: [PATCH 7/8] Add errors to non compiling snippets --- .../arrow-docs/docs/docs/generic/coproduct/README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md index 029c34b1af8..3e804049987 100644 --- a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md +++ b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md @@ -65,7 +65,11 @@ There are `coproductOf` constructor functions for each Coproduct regardless of t If we pass in a value that doesn't correspond to any types on the Coproduct, it won't compile: ```kolint:ank +import arrow.generic.* +import arrow.generic.coproduct3.coproductOf + //val apiResult = coproductOf(ServerError) +//error: type mismatch: inferred type is ServerError but String was expected ``` ##### cop @@ -82,7 +86,11 @@ val apiResult = ServerError.cop() +//error: type mismatch: inferred type is ServerError but String was expected ``` ##### fold @@ -179,6 +187,7 @@ import arrow.generic.coproduct3.Coproduct3 import arrow.generic.coproduct3.select fun handleApiResult(apiResult: Coproduct3): Unit { - //apiResult.select() Doesn't compile +// apiResult.select() +//error: type mismatch: inferred type is Coproduct3 but Coproduct3 was expected } ``` From 9e40b5bd2f17ceafff300058159f2f5a9aecd490 Mon Sep 17 00:00:00 2001 From: abergfeld Date: Tue, 28 Aug 2018 19:55:13 -0500 Subject: [PATCH 8/8] Fix snippet --- modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md index 3e804049987..71d1d5d91f7 100644 --- a/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md +++ b/modules/docs/arrow-docs/docs/docs/generic/coproduct/README.md @@ -64,7 +64,7 @@ There are `coproductOf` constructor functions for each Coproduct regardless of t If we pass in a value that doesn't correspond to any types on the Coproduct, it won't compile: -```kolint:ank +```kotlin:ank import arrow.generic.* import arrow.generic.coproduct3.coproductOf