Arrow in Kotlin
Issue: BAEL-1495
This commit is contained in:
parent
4d30ff3b43
commit
2c4cefe516
@ -87,6 +87,13 @@
|
|||||||
<artifactId>h2</artifactId>
|
<artifactId>h2</artifactId>
|
||||||
<version>${h2database.version}</version>
|
<version>${h2database.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/io.arrow-kt/arrow-core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.arrow-kt</groupId>
|
||||||
|
<artifactId>arrow-core</artifactId>
|
||||||
|
<version>0.7.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.baeldung.kotlin.arrow
|
||||||
|
|
||||||
|
import arrow.core.Either
|
||||||
|
import arrow.core.filterOrElse
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
class FunctionalErrorHandlingWithEither {
|
||||||
|
|
||||||
|
sealed class ComputeProblem {
|
||||||
|
object OddNumber : ComputeProblem()
|
||||||
|
object NotANumber : ComputeProblem()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseInput(s : String) : Either<ComputeProblem, Int> = Either.cond(s.toIntOrNull() != null, {-> s.toInt()}, {->ComputeProblem.NotANumber} )
|
||||||
|
|
||||||
|
fun isEven(x : Int) : Boolean = x % 2 == 0
|
||||||
|
|
||||||
|
fun biggestDivisor(x: Int) : Int = biggestDivisor(x, 2)
|
||||||
|
|
||||||
|
fun biggestDivisor(x : Int, y : Int) : Int {
|
||||||
|
if(x == y){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(x % y == 0){
|
||||||
|
return x / y;
|
||||||
|
}
|
||||||
|
return biggestDivisor(x, y+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSquareNumber(x : Int) : Boolean {
|
||||||
|
val sqrt: Double = sqrt(x.toDouble())
|
||||||
|
return sqrt % 1.0 == 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computeWithEither(input : String) : Either<ComputeProblem, Boolean> {
|
||||||
|
return parseInput(input)
|
||||||
|
.filterOrElse(::isEven) {->ComputeProblem.OddNumber}
|
||||||
|
.map (::biggestDivisor)
|
||||||
|
.map (::isSquareNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computeWithEitherClient(input : String) {
|
||||||
|
val computeWithEither = computeWithEither(input)
|
||||||
|
|
||||||
|
when(computeWithEither){
|
||||||
|
is Either.Right -> "The greatest divisor is square number: ${computeWithEither.b}"
|
||||||
|
is Either.Left -> when(computeWithEither.a){
|
||||||
|
is ComputeProblem.NotANumber -> "Wrong input! Not a number!"
|
||||||
|
is ComputeProblem.OddNumber -> "It is an odd number!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.baeldung.kotlin.arrow
|
||||||
|
|
||||||
|
import arrow.core.None
|
||||||
|
import arrow.core.Option
|
||||||
|
import arrow.core.Some
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
class FunctionalErrorHandlingWithOption {
|
||||||
|
|
||||||
|
fun parseInput(s : String) : Option<Int> = Option.fromNullable(s.toIntOrNull())
|
||||||
|
|
||||||
|
fun isEven(x : Int) : Boolean = x % 2 == 0
|
||||||
|
|
||||||
|
fun biggestDivisor(x: Int) : Int = biggestDivisor(x, 2)
|
||||||
|
|
||||||
|
fun biggestDivisor(x : Int, y : Int) : Int {
|
||||||
|
if(x == y){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(x % y == 0){
|
||||||
|
return x / y;
|
||||||
|
}
|
||||||
|
return biggestDivisor(x, y+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSquareNumber(x : Int) : Boolean {
|
||||||
|
val sqrt: Double = sqrt(x.toDouble())
|
||||||
|
return sqrt % 1.0 == 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computeWithOption(input : String) : Option<Boolean> {
|
||||||
|
return parseInput(input)
|
||||||
|
.filter(::isEven)
|
||||||
|
.map(::biggestDivisor)
|
||||||
|
.map(::isSquareNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computeWithOptionClient(input : String) : String{
|
||||||
|
val computeOption = computeWithOption(input)
|
||||||
|
|
||||||
|
return when(computeOption){
|
||||||
|
is None -> "Not an even number!"
|
||||||
|
is Some -> "The greatest divisor is square number: ${computeOption.t}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,143 @@
|
|||||||
|
package com.baeldung.kotlin.arrow
|
||||||
|
|
||||||
|
import arrow.core.*
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class FunctionalDataTypes {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenIdCreated_thanValueIsPresent(){
|
||||||
|
val id = Id("foo")
|
||||||
|
val justId = Id.just("foo");
|
||||||
|
|
||||||
|
Assert.assertEquals("foo", id.extract())
|
||||||
|
Assert.assertEquals(justId, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun length(s : String) : Int = s.length
|
||||||
|
|
||||||
|
fun isBigEnough(i : Int) : Boolean = i > 10
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenIdCreated_thanMapIsAssociative(){
|
||||||
|
val foo = Id("foo")
|
||||||
|
|
||||||
|
val map1 = foo.map(::length)
|
||||||
|
.map(::isBigEnough)
|
||||||
|
val map2 = foo.map { s -> isBigEnough(length(s)) }
|
||||||
|
|
||||||
|
Assert.assertEquals(map1, map2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lengthId(s : String) : Id<Int> = Id.just(length(s))
|
||||||
|
|
||||||
|
fun isBigEnoughId(i : Int) : Id<Boolean> = Id.just(isBigEnough(i))
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenIdCreated_thanFlatMapIsAssociative(){
|
||||||
|
val bar = Id("bar")
|
||||||
|
|
||||||
|
val flatMap = bar.flatMap(::lengthId)
|
||||||
|
.flatMap(::isBigEnoughId)
|
||||||
|
val flatMap1 = bar.flatMap { s -> lengthId(s).flatMap(::isBigEnoughId) }
|
||||||
|
|
||||||
|
Assert.assertEquals(flatMap, flatMap1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenOptionCreated_thanValueIsPresent(){
|
||||||
|
val factory = Option.just(42)
|
||||||
|
val constructor = Option(42)
|
||||||
|
val emptyOptional = Option.empty<Integer>()
|
||||||
|
val fromNullable = Option.fromNullable(null)
|
||||||
|
|
||||||
|
Assert.assertEquals(42, factory.getOrElse { -1 })
|
||||||
|
Assert.assertEquals(factory, constructor)
|
||||||
|
Assert.assertEquals(emptyOptional, fromNullable)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenOptionCreated_thanConstructorDifferFromFactory(){
|
||||||
|
val constructor : Option<String?> = Option(null)
|
||||||
|
val fromNullable : Option<String?> = Option.fromNullable(null)
|
||||||
|
|
||||||
|
try{
|
||||||
|
constructor.map { s -> s!!.length }
|
||||||
|
Assert.fail()
|
||||||
|
} catch (e : KotlinNullPointerException){
|
||||||
|
fromNullable.map { s->s!!.length }
|
||||||
|
}
|
||||||
|
Assert.assertNotEquals(constructor, fromNullable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun wrapper(x : Integer?) : Option<Int> = if (x == null) Option.just(-1) else Option.just(x.toInt())
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenOptionFromNullableCreated_thanItBreaksLeftIdentity(){
|
||||||
|
val optionFromNull = Option.fromNullable(null)
|
||||||
|
|
||||||
|
Assert.assertNotEquals(optionFromNull.flatMap(::wrapper), wrapper(null))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenEitherCreated_thanOneValueIsPresent(){
|
||||||
|
val rightOnly : Either<String,Int> = Either.right(42)
|
||||||
|
val leftOnly : Either<String,Int> = Either.left("foo")
|
||||||
|
|
||||||
|
Assert.assertTrue(rightOnly.isRight())
|
||||||
|
Assert.assertTrue(leftOnly.isLeft())
|
||||||
|
Assert.assertEquals(42, rightOnly.getOrElse { -1 })
|
||||||
|
Assert.assertEquals(-1, leftOnly.getOrElse { -1 })
|
||||||
|
|
||||||
|
Assert.assertEquals(0, rightOnly.map { it % 2 }.getOrElse { -1 })
|
||||||
|
Assert.assertEquals(-1, leftOnly.map { it % 2 }.getOrElse { -1 })
|
||||||
|
Assert.assertTrue(rightOnly.flatMap { Either.Right(it % 2) }.isRight())
|
||||||
|
Assert.assertTrue(leftOnly.flatMap { Either.Right(it % 2) }.isLeft())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenEvalNowUsed_thenMapEvaluatedLazily(){
|
||||||
|
val now = Eval.now(1)
|
||||||
|
Assert.assertEquals(1, now.value())
|
||||||
|
|
||||||
|
var counter : Int = 0
|
||||||
|
val map = now.map { x -> counter++; x+1 }
|
||||||
|
Assert.assertEquals(0, counter)
|
||||||
|
|
||||||
|
val value = map.value()
|
||||||
|
Assert.assertEquals(2, value)
|
||||||
|
Assert.assertEquals(1, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenEvalLaterUsed_theResultIsMemoized(){
|
||||||
|
var counter : Int = 0
|
||||||
|
val later = Eval.later { counter++; counter }
|
||||||
|
Assert.assertEquals(0, counter)
|
||||||
|
|
||||||
|
val firstValue = later.value()
|
||||||
|
Assert.assertEquals(1, firstValue)
|
||||||
|
Assert.assertEquals(1, counter)
|
||||||
|
|
||||||
|
val secondValue = later.value()
|
||||||
|
Assert.assertEquals(1, secondValue)
|
||||||
|
Assert.assertEquals(1, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenEvalAlwaysUsed_theResultIsNotMemoized(){
|
||||||
|
var counter : Int = 0
|
||||||
|
val later = Eval.always { counter++; counter }
|
||||||
|
Assert.assertEquals(0, counter)
|
||||||
|
|
||||||
|
val firstValue = later.value()
|
||||||
|
Assert.assertEquals(1, firstValue)
|
||||||
|
Assert.assertEquals(1, counter)
|
||||||
|
|
||||||
|
val secondValue = later.value()
|
||||||
|
Assert.assertEquals(2, secondValue)
|
||||||
|
Assert.assertEquals(2, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.baeldung.kotlin.arrow
|
||||||
|
|
||||||
|
import arrow.core.Either
|
||||||
|
import com.baeldung.kotlin.arrow.FunctionalErrorHandlingWithEither.ComputeProblem.NotANumber
|
||||||
|
import com.baeldung.kotlin.arrow.FunctionalErrorHandlingWithEither.ComputeProblem.OddNumber
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class FunctionalErrorHandlingWithEitherTest {
|
||||||
|
|
||||||
|
val operator = FunctionalErrorHandlingWithEither()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenInvalidInput_whenComputeInvoked_NotANumberIsPresent(){
|
||||||
|
val computeWithEither = operator.computeWithEither("bar")
|
||||||
|
|
||||||
|
Assert.assertTrue(computeWithEither.isLeft())
|
||||||
|
when(computeWithEither){
|
||||||
|
is Either.Left -> when(computeWithEither.a){
|
||||||
|
NotANumber -> "Ok."
|
||||||
|
else -> Assert.fail()
|
||||||
|
}
|
||||||
|
else -> Assert.fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenOddNumberInput_whenComputeInvoked_OddNumberIsPresent(){
|
||||||
|
val computeWithEither = operator.computeWithEither("121")
|
||||||
|
|
||||||
|
Assert.assertTrue(computeWithEither.isLeft())
|
||||||
|
when(computeWithEither){
|
||||||
|
is Either.Left -> when(computeWithEither.a){
|
||||||
|
OddNumber -> "Ok."
|
||||||
|
else -> Assert.fail()
|
||||||
|
}
|
||||||
|
else -> Assert.fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenEvenNumberWithoutSquare_whenComputeInvoked_OddNumberIsPresent(){
|
||||||
|
val computeWithEither = operator.computeWithEither("100")
|
||||||
|
|
||||||
|
Assert.assertTrue(computeWithEither.isRight())
|
||||||
|
when(computeWithEither){
|
||||||
|
is Either.Right -> when(computeWithEither.b){
|
||||||
|
false -> "Ok."
|
||||||
|
else -> Assert.fail()
|
||||||
|
}
|
||||||
|
else -> Assert.fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenEvenNumberWithSquare_whenComputeInvoked_OddNumberIsPresent(){
|
||||||
|
val computeWithEither = operator.computeWithEither("98")
|
||||||
|
|
||||||
|
Assert.assertTrue(computeWithEither.isRight())
|
||||||
|
when(computeWithEither){
|
||||||
|
is Either.Right -> when(computeWithEither.b){
|
||||||
|
true -> "Ok."
|
||||||
|
else -> Assert.fail()
|
||||||
|
}
|
||||||
|
else -> Assert.fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.baeldung.kotlin.arrow
|
||||||
|
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class FunctionalErrorHandlingWithOptionTest {
|
||||||
|
|
||||||
|
val operator = FunctionalErrorHandlingWithOption()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenInvalidInput_thenErrorMessageIsPresent(){
|
||||||
|
val useComputeOption = operator.computeWithOptionClient("foo")
|
||||||
|
Assert.assertEquals("Not an even number!", useComputeOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenOddNumberInput_thenErrorMessageIsPresent(){
|
||||||
|
val useComputeOption = operator.computeWithOptionClient("539")
|
||||||
|
Assert.assertEquals("Not an even number!",useComputeOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenEvenNumberInputWithNonSquareNum_thenFalseMessageIsPresent(){
|
||||||
|
val useComputeOption = operator.computeWithOptionClient("100")
|
||||||
|
Assert.assertEquals("The greatest divisor is square number: false",useComputeOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenEvenNumberInputWithSquareNum_thenTrueMessageIsPresent(){
|
||||||
|
val useComputeOption = operator.computeWithOptionClient("242")
|
||||||
|
Assert.assertEquals("The greatest divisor is square number: true",useComputeOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user