├── settings.gradle ├── .gitignore ├── images └── logo.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ └── java │ │ ├── recursion │ │ ├── fibonacci │ │ │ ├── FibonacciKata.kt │ │ │ └── README.md │ │ ├── adding │ │ │ └── multiplying │ │ │ │ ├── AddingMultiplyingKata.kt │ │ │ │ └── README.md │ │ └── reduce │ │ │ ├── ReduceKata.kt │ │ │ └── README.md │ │ ├── functional │ │ ├── abstractions │ │ │ ├── monad │ │ │ │ ├── MonadKata.kt │ │ │ │ └── README.md │ │ │ ├── monoid │ │ │ │ ├── MonoidKata.kt │ │ │ │ └── README.md │ │ │ ├── applicative │ │ │ │ ├── ApplicativeKata.kt │ │ │ │ └── README.md │ │ │ └── functor │ │ │ │ ├── FunctorKata.kt │ │ │ │ └── README.md │ │ └── transforms │ │ │ ├── reduce │ │ │ ├── ReduceKata.kt │ │ │ └── README.md │ │ │ ├── map │ │ │ ├── MapKata.kt │ │ │ └── README.md │ │ │ ├── mapreduce │ │ │ ├── MapReduceKata.kt │ │ │ └── README.md │ │ │ ├── filtermap │ │ │ ├── FilterMapKata.kt │ │ │ └── README.md │ │ │ ├── flatMap │ │ │ ├── simple │ │ │ │ ├── FlatMapSimpleKata.kt │ │ │ │ └── README.md │ │ │ └── advanced │ │ │ │ ├── FlatMapAdvancedKata.kt │ │ │ │ └── README.md │ │ │ ├── zip │ │ │ ├── ZipKata.kt │ │ │ └── README.md │ │ │ ├── Models.kt │ │ │ └── DataSource.kt │ │ └── functions │ │ ├── currying │ │ ├── CurryingKata.kt │ │ └── README.md │ │ ├── partial │ │ ├── PartialKata.kt │ │ └── README.md │ │ └── cps │ │ ├── CPSKata.kt │ │ └── README.md └── test │ └── java │ ├── Extensions.kt │ └── Tests.kt ├── LICENSE.md ├── gradlew.bat ├── README.md └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gradle 3 | build 4 | *.iml 5 | *.DS_STORE 6 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunctionalKotlin/katas/HEAD/images/logo.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunctionalKotlin/katas/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/recursion/fibonacci/FibonacciKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package recursion.fibonacci 4 | 5 | fun fibonacci(n: Int): Int = n 6 | -------------------------------------------------------------------------------- /src/main/java/functional/abstractions/monad/MonadKata.kt: -------------------------------------------------------------------------------- 1 | package functional.abstractions.monad 2 | 3 | import functional.abstractions.functor.Try 4 | 5 | fun Try.flatMap(any: Any): Any = throw NotImplementedError() -------------------------------------------------------------------------------- /src/main/java/functional/abstractions/monoid/MonoidKata.kt: -------------------------------------------------------------------------------- 1 | package functional.abstractions.monoid 2 | 3 | import functional.abstractions.functor.Try 4 | 5 | fun Try.plus(any1: Any, any2: Any): Any = throw NotImplementedError() -------------------------------------------------------------------------------- /src/main/java/recursion/adding/multiplying/AddingMultiplyingKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package recursion.adding.multiplying 4 | 5 | fun canReach(n: Int, add: Int, multiply: Int): Boolean = false -------------------------------------------------------------------------------- /src/main/java/recursion/reduce/ReduceKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package recursion.reduce 4 | 5 | fun List.reduceKt(operation: (acc: A, A) -> A): A = 6 | throw NotImplementedError() 7 | -------------------------------------------------------------------------------- /src/main/java/functions/currying/CurryingKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functions.currying 4 | 5 | fun add(x: Int, y: Int): Int = x + y 6 | 7 | fun ((A, A) -> A).curried(): (A) -> (A) -> A = { { it } } -------------------------------------------------------------------------------- /src/main/java/functional/transforms/reduce/ReduceKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functional.transforms.reduce 4 | 5 | import functional.transforms.Movie 6 | 7 | fun getLargestRating(list: List): Double = 0.0 -------------------------------------------------------------------------------- /src/main/java/functional/transforms/map/MapKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functional.transforms.map 4 | 5 | import functional.transforms.Movie 6 | 7 | fun getMovies(movies: List): Map = emptyMap() -------------------------------------------------------------------------------- /src/main/java/functions/partial/PartialKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functions.partial 4 | 5 | data class Element(val type: String, val position: String, val id: Int) 6 | 7 | lateinit var partial: (Int) -> Element -------------------------------------------------------------------------------- /src/main/java/functional/transforms/mapreduce/MapReduceKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functional.transforms.mapreduce 4 | 5 | import functional.transforms.Movie 6 | 7 | fun getUrlOfLargestBoxArt(list: List): String = "" -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | It is strictly prohibited to copy, play, transmit, publish or modify any material included in the website FunctionalKotlin.com or in any other related platform except with express written permission of the authors. -------------------------------------------------------------------------------- /src/main/java/functions/cps/CPSKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functions.cps 4 | 5 | data class FailureException(override val message: String): Exception(message) 6 | 7 | fun performOperationCPS(any1: Any, any2: Any, any3: Any) { 8 | } -------------------------------------------------------------------------------- /src/main/java/functional/transforms/filtermap/FilterMapKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functional.transforms.filtermap 4 | 5 | import functional.transforms.Movie 6 | 7 | fun getFiveRatingMoviesIds(movies: List): List = emptyList() -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jul 27 19:31:35 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-rc-2-all.zip 7 | -------------------------------------------------------------------------------- /src/main/java/functional/transforms/flatMap/simple/FlatMapSimpleKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functional.transforms.flatMap.simple 4 | 5 | import functional.transforms.MovieList 6 | 7 | fun getAllMoviesIds(list: List): List = emptyList() 8 | -------------------------------------------------------------------------------- /src/main/java/functional/transforms/flatMap/advanced/FlatMapAdvancedKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functional.transforms.flatMap.advanced 4 | 5 | import functional.transforms.MovieList 6 | 7 | fun getAllMoviesInformation(list: List): List> = 8 | emptyList() -------------------------------------------------------------------------------- /src/main/java/functional/transforms/zip/ZipKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functional.transforms.zip 4 | 5 | import functional.transforms.Cast 6 | import functional.transforms.Movie 7 | 8 | fun getFirstActorMap(movies: List, casts: List): 9 | Map = emptyMap() -------------------------------------------------------------------------------- /src/main/java/functional/abstractions/applicative/ApplicativeKata.kt: -------------------------------------------------------------------------------- 1 | package functional.abstractions.applicative 2 | 3 | import functional.abstractions.functor.Try 4 | import functional.abstractions.functor.map 5 | import functional.abstractions.monad.flatMap 6 | 7 | fun Try.apply(any: Any): Any = throw NotImplementedError() 8 | 9 | infix fun ((A) -> B).map(any: Any): Any = throw NotImplementedError() 10 | 11 | infix fun Try<(A) -> B>.ap(any: Any): Any = throw NotImplementedError() 12 | -------------------------------------------------------------------------------- /src/main/java/functional/transforms/Models.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functional.transforms 4 | 5 | data class Movie( 6 | val id: Int, val title: String, val rating: Double, 7 | val interestingMoments: List, 8 | private val boxArtsWidths: List = listOf(150, 200)) { 9 | 10 | val boxArts: List = boxArtsWidths.map { 11 | BoxArt(it, 200, title.replace(" ", "")) 12 | } 13 | } 14 | 15 | data class BoxArt(val width: Int, val height: Int, private val name: String) { 16 | val url = "a_place_in_the_cloud/$name$width.jpg" 17 | } 18 | 19 | data class InterestingMoment(val type: String, val time: Int) 20 | 21 | data class Cast(val id: Int, val people: List) 22 | 23 | data class Person(val name: String, val role: String) 24 | 25 | data class MovieList(val name: String, val movies: List) -------------------------------------------------------------------------------- /src/test/java/Extensions.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | import functional.transforms.BoxArt 4 | import functional.transforms.Cast 5 | import functional.abstractions.functor.PredicateException 6 | import java.util.Random 7 | 8 | fun ClosedRange.random() = Random().nextInt(endInclusive - start) + start 9 | 10 | fun List.boxArt150() = firstOrNull { it.width == 150 } 11 | 12 | fun List.biggestBoxArt(): BoxArt = reduce { acc, boxArt -> 13 | if (acc.area > boxArt.area) acc 14 | else boxArt 15 | } 16 | 17 | val BoxArt.area: Int 18 | get() = height * width 19 | 20 | fun Cast.nameOfFirstActor(): String? = people.firstOrNull() 21 | ?.name 22 | ?.substringBefore(" ") 23 | 24 | fun ((A, B) -> C).curried(): (A) -> (B) -> C = 25 | { a -> { b -> this(a, b) } } 26 | 27 | fun throwException(): String = throw PredicateException("Error") -------------------------------------------------------------------------------- /src/main/java/functional/abstractions/monad/README.md: -------------------------------------------------------------------------------- 1 | # Task 2 | 3 | Your task is to implement a monad for the `Try` type. 4 | 5 | For that, you should create the method `flatMap`. 6 | 7 | If you want to use the provided tests this method must be called `flatMap` and must be inside the `functional.abstractions.monad` package. 8 | 9 | ## Params 10 | 11 | - `transform`: a function that takes an `A` and return a `Try` 12 | 13 | ## Example 14 | 15 | ```kotlin 16 | Try { "hi" }.flatMap { 17 | Try { "hello $it" } 18 | } // Success("hello hi") 19 | 20 | Try { "hi" }.flatMap { 21 | throw Exception() 22 | } // Failure(Exception) 23 | ``` 24 | 25 | ## Solution 26 | 27 |
Reveal

28 | 29 | --- 30 | ```kotlin 31 | fun Try.flatMap(transform: (A) -> Try): Try = 32 | fold({Failure(it)}, {transform(it)}) 33 | ``` 34 | 35 |

36 | 37 | --- 38 | 39 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/abstractions/functor/FunctorKata.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functional.abstractions.functor 4 | 5 | data class PredicateException(override val message: String) : Exception(message) 6 | 7 | typealias Failure = Try.Failure 8 | typealias Success = Try.Success 9 | 10 | sealed class Try { 11 | 12 | companion object { 13 | operator fun invoke(f: () -> A): Try = throw NotImplementedError() 14 | } 15 | 16 | class Success: Try() 17 | class Failure: Try() 18 | 19 | } 20 | 21 | fun Try.filter(any: Any): Any = throw NotImplementedError() 22 | 23 | fun Try.fold(any1: Any, any2: Any): Any = throw NotImplementedError() 24 | 25 | fun Try.get(): Any = throw NotImplementedError() 26 | 27 | fun Try.isFailure(): Any = throw NotImplementedError() 28 | 29 | fun Try.isSuccess(): Any = throw NotImplementedError() 30 | 31 | fun Try.map(any: Any): Any = throw NotImplementedError() -------------------------------------------------------------------------------- /src/main/java/functions/partial/README.md: -------------------------------------------------------------------------------- 1 | # Task 2 | 3 | Your task is to implement a partially applied version of the following data class constructor: 4 | 5 | `data class Element(val type: String, val position: String, val id: Int)` 6 | 7 | We want a partially applied function for elements of type "Blog" and position "Fixed", that allows us to create elements based on the element's id. 8 | 9 | If you want to use the provided tests the function must be a variable called `partial` and must be inside the `functions.partial` package. 10 | 11 | # Conditions 12 | 13 | - You can't alter the `Element` data class nor directly nor via extension functions. 14 | - You can't create a new function (with `fun`) to achieve the purpose of the kata. 15 | 16 | ## Solution 17 | 18 |
Reveal

19 | 20 | --- 21 | ```kotlin 22 | val partial: (Int) -> Element = { Element("Blog", "Fixed", it) } 23 | ``` 24 | 25 |

26 | 27 | --- 28 | 29 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/transforms/reduce/README.md: -------------------------------------------------------------------------------- 1 | ## Task 2 | 3 | Your task is to implement a function that receives a list of `Movie` and returns the largest rating. 4 | 5 | If you want to use the provided tests the function must be called `getLargestRating` and must be inside the `functional.transforms.reduce` package. 6 | 7 | ## Input 8 | 9 | Use the `moviesDemo` field inside [DataSource.kt](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/DataSource.kt) file. 10 | 11 | ## Example 12 | 13 | ```kotlin 14 | getLargestRating(moviesDemo) // 5.0 15 | ``` 16 | 17 | ## Conditions 18 | 19 | * Do not use any loops. 20 | 21 | ## Solution 22 | 23 |
Reveal

24 | 25 | --- 26 | ```kotlin 27 | fun getLargestRating(list: List): Double = list.reduce { acc, movie -> 28 | if (acc.rating > movie.rating) acc 29 | else movie 30 | }.rating 31 | ``` 32 | 33 |

34 | 35 | --- 36 | 37 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functions/currying/README.md: -------------------------------------------------------------------------------- 1 | # Task 2 | 3 | Your task is to implement an extension function for functions that receive two elements of the same type and return an element of that same type. 4 | 5 | `(A, A) -> A` 6 | 7 | Like, for example: Int::plus, String::concat, etc. 8 | 9 | This extension functions must return a currified version of the function been extended. 10 | 11 | If you want to use the provided tests the function must be called `curried` and must be inside the `functions.currying` package. 12 | 13 | ## Example 14 | 15 | ```kotlin 16 | fun add(x: Int, y: Int): Int = x + y 17 | 18 | val curried = ::add.curried() // (Int) -> (Int) -> Int 19 | 20 | curried(1) // (Int) -> Int 21 | curried(1, 2) // 3 22 | ``` 23 | 24 | ## Solution 25 | 26 |
Reveal

27 | 28 | --- 29 | ```kotlin 30 | fun ((A, A) -> A).curried(): (A) -> (A) -> A = { 31 | a1 -> { a2 -> this(a1, a2) } 32 | } 33 | ``` 34 | 35 |

36 | 37 | --- 38 | 39 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/transforms/filtermap/README.md: -------------------------------------------------------------------------------- 1 | ## Task 2 | 3 | Your task is to implement a function that receives a list of `Movie` and returns a `List` containing the ids of the movies whose rating is `5.0`. 4 | 5 | If you want to use the provided tests the function must be called `getFiveRatingMoviesIds` and must be inside the `functional.transforms.filtermap` package. 6 | 7 | ## Input 8 | 9 | Use the `moviesDemo` field inside [DataSource.kt](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/DataSource.kt) file. 10 | 11 | ## Example 12 | 13 | ```kotlin 14 | getFiveRatingMoviesIds(moviesDemo) // [654356453, 675465] 15 | ``` 16 | 17 | ## Conditions 18 | 19 | * Do not use any loops. 20 | 21 | ## Solution 22 | 23 |
Reveal

24 | 25 | --- 26 | ```kotlin 27 | fun getFiveRatingMoviesIds(movies: List): List = movies.filter { 28 | it.rating == 5.0 29 | }.map { 30 | it.id 31 | } 32 | ``` 33 | 34 |

35 | 36 | --- 37 | 38 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/transforms/flatMap/simple/README.md: -------------------------------------------------------------------------------- 1 | ## Task 2 | 3 | Your task is to implement a function that receives a list of `MovieList` (a list of lists of `Movie`) and returns a `List` containing the ids of the movies. 4 | 5 | If you want to use the provided tests the function must be called `getAllMoviesIds` and must be inside the `functional.transforms.flatMap.simple` package. 6 | 7 | ## Input 8 | 9 | Use the `movieListsDemo` field inside [DataSource.kt](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/DataSource.kt) file. 10 | 11 | ## Example 12 | 13 | ```kotlin 14 | getAllMoviesIds(movieListsDemo) // [70111470, 654356453, 65432445, 675465] 15 | ``` 16 | 17 | ## Conditions 18 | 19 | * Do not use any loops. 20 | 21 | ## Solution 22 | 23 |
Reveal

24 | 25 | --- 26 | ```kotlin 27 | fun getAllMoviesIds(list: List): List = list.flatMap { 28 | it.movies 29 | }.map { 30 | it.id 31 | } 32 | ``` 33 | 34 |

35 | 36 | --- 37 | 38 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/abstractions/monoid/README.md: -------------------------------------------------------------------------------- 1 | # Task 2 | 3 | Your task is to implement a monoid for the `Try` type. 4 | 5 | For that, you should create the method `plus`. 6 | 7 | If you want to use the provided tests this method must be called `plus` and must be inside the `functional.abstractions.monoid` package. 8 | 9 | ## Params 10 | 11 | - `aTry`: a `Try` of the same type. 12 | - `f`: a function that takes two `A` and return a `B` 13 | 14 | ## Example 15 | 16 | ```kotlin 17 | Try { "hello" }.plus(Try { " hi" }) { 18 | a1, a2 -> a1 + a2 19 | } // Success("hello hi") 20 | 21 | Try { 3 }.plus(Try { " hi".toInt() }) { 22 | a1, a2 -> a1 + a2 23 | } // Failure(NumberFormatException) 24 | ``` 25 | 26 | ## Solution 27 | 28 |
Reveal

29 | 30 | --- 31 | ```kotlin 32 | fun Try.plus(aTry: Try, f: (A, A) -> B): Try = 33 | fold( 34 | { Failure(it) }, 35 | { value -> aTry.fold( 36 | { Failure(it) }, 37 | { Success(f(value, it)) })}) 38 | ``` 39 | 40 |

41 | 42 | --- 43 | 44 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/transforms/map/README.md: -------------------------------------------------------------------------------- 1 | ## Task 2 | 3 | Your task is to implement a function that receives a list of `Movie` and returns a `Map` where the keys are the movie ids and the values are the movie titles. 4 | 5 | If you want to use the provided tests the function must be called `getMovies` and must be inside the `functional.transforms.map` package. 6 | 7 | ## Input 8 | 9 | Use the `moviesDemo` field inside [DataSource.kt](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/DataSource.kt) file. 10 | 11 | ## Example 12 | 13 | ```kotlin 14 | getMovies(moviesDemo) 15 | 16 | /* 17 | { 18 | 70111470=Die Hard, 19 | 654356453=Bad Boys, 20 | 65432445=The Chamber, 21 | 675465=Fracture 22 | } 23 | */ 24 | ``` 25 | 26 | ## Conditions 27 | 28 | * Do not use any loops. 29 | 30 | ## Solution 31 | 32 |
Reveal

33 | 34 | --- 35 | ```kotlin 36 | fun getMovies(movies: List): Map = movies.map { 37 | it.id to it.title 38 | }.toMap() 39 | ``` 40 | 41 |

42 | 43 | --- 44 | 45 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/transforms/mapreduce/README.md: -------------------------------------------------------------------------------- 1 | ## Task 2 | 3 | Your task is to implement a function that receives a list of `Movie` and returns the url of the largest box art. 4 | 5 | If you want to use the provided tests the function must be called `getUrlOfLargestBoxArt` and must be inside the `functional.transforms.mapreduce` package. 6 | 7 | ## Input 8 | 9 | Use the `moviesDemo` field inside [DataSource.kt](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/DataSource.kt) file. 10 | 11 | ## Example 12 | 13 | ```kotlin 14 | getUrlOfLargestBoxArt(moviesDemo) // a_place_in_the_cloud/Fracture300.jpg 15 | ``` 16 | 17 | ## Conditions 18 | 19 | * Do not use any loops. 20 | 21 | ## Solution 22 | 23 |
Reveal

24 | 25 | --- 26 | ```kotlin 27 | fun getUrlOfLargestBoxArt(list: List): String = list.flatMap { 28 | it.boxArts 29 | }.biggestBoxArt().url 30 | 31 | private fun List.biggestBoxArt(): BoxArt = reduce { acc, boxArt -> 32 | if (acc.area > boxArt.area) acc 33 | else boxArt 34 | } 35 | 36 | private val BoxArt.area: Int 37 | get() = height * width 38 | ``` 39 | 40 |

41 | 42 | --- 43 | 44 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/abstractions/applicative/README.md: -------------------------------------------------------------------------------- 1 | # Task 2 | 3 | Your task is to implement an applicative for the `Try` type. 4 | 5 | For that, you should create the methods: 6 | 7 | - `apply`: a function that receives a `Try<(A) -> B>` and returns a `Try`. 8 | - `map`: and infix function that allows to `map` a function `(A) -> B` into a `Try
` to get a `Try`. 9 | - `ap`: and infix function that allow to apply a `Try<(A) -> B>` into a `Try` to get a `Try`. 10 | 11 | If you want to use the provided tests these methods must be called as they are listed and must be inside the `functional.abstractions.applicative` package. 12 | 13 | ## Example 14 | 15 | ```kotlin 16 | data class Class(val a: String, val b: Int) 17 | 18 | ::Class.curried() map 19 | Try { "hello" } ap 20 | Try { 3 } // Success(Class("hello", 3)) 21 | 22 | ::Class.curried() map 23 | Try { "hello" } ap 24 | Try { "hi".toInt() } // Failure(NumberFormatException) 25 | ``` 26 | 27 | ## Solution 28 | 29 |
Reveal

30 | 31 | --- 32 | ```kotlin 33 | fun Try.apply( 34 | tryAB: Try<(A) -> B>): Try = 35 | flatMap { a -> tryAB.map { it(a) } } 36 | 37 | infix fun ((A) -> B).map(aTry: Try): Try = aTry.map(this) 38 | 39 | infix fun Try<(A) -> B>.ap(aTry: Try): Try = aTry.apply(this) 40 | ``` 41 | 42 |

43 | 44 | --- 45 | 46 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functions/cps/README.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | In imperative programming is really typical to implement conditional paths using callbacks/listeners. 4 | 5 | ```kotlin 6 | data class FailureException(override val message: String): Exception(message) 7 | 8 | interface Listener { 9 | fun onSuccess(int: Int) 10 | fun onFailure(ex: FailureException) 11 | } 12 | 13 | fun performOperation(boolean: Boolean, listener: Listener) { 14 | if (boolean) listener.onSuccess(42) 15 | else listener.onFailure(FailureException("Error")) 16 | } 17 | ``` 18 | 19 | # Task 20 | 21 | Your task is to implement a function (similar to `performOperation`) but that uses CPS to manage both branches of the execution (success and failure) instead of a `Listener`. 22 | 23 | As a bonus, you should implement this function so that instead of receiving a `Boolean` as the check of the computation, it should receive a function, that allows to lazily evaluate the check. 24 | 25 | If you want to use the provided tests the function must be called `performOperationCPS` and must be inside the `functions.cps` package. 26 | 27 | ## Solution 28 | 29 |
Reveal

30 | 31 | --- 32 | ```kotlin 33 | fun performOperationCPS( 34 | check: () -> Boolean, 35 | onSuccess: (Int) -> Unit, 36 | onError: (FailureException) -> Unit) { 37 | 38 | if (check()) onSuccess(42) 39 | else onError(FailureException("Error")) 40 | } 41 | ``` 42 | 43 |

44 | 45 | --- 46 | 47 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/transforms/zip/README.md: -------------------------------------------------------------------------------- 1 | ## Task 2 | 3 | Your task is to implement a function that receives a list of `Movie` and a list of `Cast` and returns a map whose keys are the movie titles and the values are the movie first actor's name (not full name). 4 | 5 | In order to use the `zip` function developers can assume the two lists contain information about the same movies, in the same order. 6 | 7 | If you want to use the provided tests the function must be called `getFirstActorMap` and must be inside the `functional.transforms.zip` package. 8 | 9 | ## Input 10 | 11 | Use the `moviesDemo` and `castsDemo` fields inside [DataSource.kt](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/DataSource.kt) file. 12 | 13 | ## Example 14 | 15 | ```kotlin 16 | getFirstActorMap(moviesDemo, castsDemo) 17 | /* 18 | { 19 | Die Hard=Bruce, 20 | Bad Boys=Will, 21 | The Chamber=Gene, 22 | Fracture=Ryan 23 | } 24 | */ 25 | ``` 26 | 27 | ## Conditions 28 | 29 | * Do not use any loops. 30 | 31 | ## Solution 32 | 33 |
Reveal

34 | 35 | --- 36 | ```kotlin 37 | fun getUrlOfLargestBoxArt(list: List): String = list.flatMap { 38 | it.boxArts 39 | }.biggestBoxArt().url 40 | 41 | private fun List.biggestBoxArt(): BoxArt = reduce { acc, boxArt -> 42 | if (acc.area > boxArt.area) acc 43 | else boxArt 44 | } 45 | 46 | private val BoxArt.area: Int 47 | get() = height * width 48 | ``` 49 | 50 |

51 | 52 | --- 53 | 54 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/recursion/adding/multiplying/README.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | **Question:** Can the number `59` be get from the number `1`, by adding `5` or multiplying by `7`? 4 | 5 | **Answer:** `59 = 1 x 7 x 7 + 5 + 5` 6 | 7 | # Task 8 | 9 | Your task is to implement the pure function: 10 | 11 | `canReach(number, add, multiply): Boolean` 12 | 13 | It must return a boolean value, indicating if it's possible to reach the number `1`, by either adding `add` or multiplying by `multiply`. 14 | 15 | You must implement this function using recursion. 16 | 17 | If you want to use the provided tests the function must be called `canReach` and must be inside the `recursion.adding.multiplying` package. 18 | 19 | ## Arguments 20 | 21 | * number: the number to reach from `1`. 22 | * add: the number that can be added in order to reach `number`. 23 | * multiply: the number by which you can multiply in order to reach `number`. 24 | 25 | ## Example 26 | 27 | ```kotlin 28 | canReach(59, 5, 7) // true 29 | canReach(10, 5, 3) // false 30 | ``` 31 | 32 | ## Conditions 33 | 34 | * Do not use any loops. 35 | 36 | ## Solution 37 | 38 |
Reveal

39 | 40 | --- 41 | ```kotlin 42 | fun canReach(n: Int, add: Int, multiply: Int): Boolean = if (n <= 1) { 43 | n == 1 44 | } else { 45 | if (n % multiply == 0 && multiply > 1) { 46 | canReach(n / multiply, add, multiply) || 47 | canReach(n - add, add, multiply) 48 | } else { 49 | canReach(n - add, add, multiply) 50 | } 51 | } 52 | ``` 53 | 54 |

55 | 56 | --- 57 | 58 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/recursion/fibonacci/README.md: -------------------------------------------------------------------------------- 1 |
2 | Fibonacci Spiral 3 |
4 | 5 | # Summary 6 | 7 | **Fibonacci** series is a series of natural numbers where the next number is the sum of the previous two numbers: 8 | 9 | ``` 10 | f(n) = f(n-1) + f(n-2) 11 | ``` 12 | 13 | The first two numbers in the **Fibonacci** series are always `1`, `1`. 14 | 15 | [Read more about Fibonacci sequence](https://en.wikipedia.org/wiki/Fibonacci_number) 16 | 17 | 18 | # Task 19 | 20 | Your task is to implement the pure function: 21 | 22 | `fibonacci(index): Int` 23 | 24 | It must return the the **Fibonacci** number inside the position `index`. 25 | 26 | You must implement this function using tail-recursion. 27 | 28 | If you want to use the provided tests the function must be called `fibonacci` and must be inside the `recursion.fibonacci` package. 29 | 30 | ## Arguments 31 | 32 | * number: the **Fibonacci** number's position. 33 | 34 | ## Example 35 | 36 | ```kotlin 37 | (1..12).forEach { print("${fibonacci(it)} ") } // 1 1 2 3 5 8 13 21 34 55 89 144 38 | ``` 39 | 40 | ## Conditions 41 | 42 | * Do not use any loops. 43 | * You can use helper functions to achieve tail-recursion. 44 | 45 | ## Solution 46 | 47 |
Reveal

48 | 49 | --- 50 | ```kotlin 51 | tailrec fun fibonacci(n: Int, a: Int = 1, b: Int = 1): Int = 52 | if (n > 0) fibonacci(n - 1, b, a + b) else a 53 | ``` 54 | 55 |

56 | 57 | --- 58 | 59 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/transforms/DataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | package functional.transforms 4 | 5 | val moviesDemo: List = listOf( 6 | Movie(70111470, "Die Hard", 4.0, listOf( 7 | InterestingMoment("End", 213432), InterestingMoment("Middle", 64534), 8 | InterestingMoment("Start", 323133))), 9 | Movie(654356453, "Bad Boys", 5.0, listOf( 10 | InterestingMoment("End", 54654754), InterestingMoment("Middle", 43524243), 11 | InterestingMoment("Start", 6575665))), 12 | Movie(65432445, "The Chamber", 4.0, listOf( 13 | InterestingMoment("End", 132423), InterestingMoment("Middle", 54637425), 14 | InterestingMoment("Start", 3452343))), 15 | Movie(675465, "Fracture", 5.0, listOf( 16 | InterestingMoment("End", 45632456), InterestingMoment("Middle", 234534), 17 | InterestingMoment("Start", 3453434)), listOf(150, 200, 300))) 18 | 19 | val movieListsDemo: List = listOf( 20 | MovieList("New Releases", moviesDemo.subList(0, 2)), 21 | MovieList("Dramas", moviesDemo.subList(2, 4))) 22 | 23 | val castsDemo: List = listOf( 24 | Cast(70111470, 25 | listOf(Person("Bruce Willis", "John McClane"), 26 | Person("Alan Rickman", "Hans Gruber"))), 27 | Cast(654356453, 28 | listOf(Person("Will Smith", "Mike Lowrey"), 29 | Person("Martin Lawrence", "Marcus Burnett"))), 30 | Cast(65432445, 31 | listOf(Person("Gene Hackman", "Sam Cayhall"), 32 | Person("Chris O'Donnell", "Adam Hall"))), 33 | Cast(675465, 34 | listOf(Person("Ryan Gosling", "Willy Beachum"), 35 | Person("Anthony Hopkins", "Ted Crawford")))) 36 | -------------------------------------------------------------------------------- /src/main/java/recursion/reduce/README.md: -------------------------------------------------------------------------------- 1 | # Task 2 | 3 | Implement `List.reduce` using recursion. 4 | 5 | `reduce` behaves exactly likes `fold`, but instead of receiving an accumulator it uses the first element of the list as the initial value. 6 | 7 | If you want to use the provided tests the function must be called `reduceKt` and must be inside the `recursion.reduce` package. 8 | 9 | ## Arguments 10 | 11 | * operation: Function to use as the reduction step. Like the `List.reduce` `operation` parameter, it should be a function with the following type: `(A, A) -> A` where `A` is the type of the elements of the collection. 12 | 13 | ## Example 14 | 15 | ```kotlin 16 | val list = listOf("functional", "hub", ".", "com") 17 | 18 | 19 | list.reduceKt(String::plus) // "functionalhub.com" 20 | ``` 21 | 22 | ## Conditions 23 | 24 | * Do not use any loops. 25 | * Do not use any `List` methods like `fold` or `reduce` (but you can create them). 26 | 27 | ## Solution 28 | 29 | 50 | 51 | --- 52 | 53 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/transforms/flatMap/advanced/README.md: -------------------------------------------------------------------------------- 1 | ## Task 2 | 3 | Your task is to implement a function that receives a list of `MovieList` (a list of lists of `Movie`) and returns a `List` containing a map with the following keys: 4 | 5 | - For key `id` the movie's id. 6 | - For key `title` the movie's title. 7 | - For key `boxArt` the movie's 150x200 box art (if any). 8 | 9 | If you want to use the provided tests the function must be called `getAllMoviesInformation` and must be inside the `functional.transforms.flatMap.advanced` package. 10 | 11 | ## Input 12 | 13 | Use the `movieListsDemo` field inside [DataSource.kt](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/DataSource.kt) file. 14 | 15 | ## Example 16 | 17 | ```kotlin 18 | getAllMoviesInformation(movieListsDemo) 19 | 20 | /* 21 | [ 22 | { 23 | id=70111470, 24 | title=Die Hard, 25 | boxArt=BoxArt(width=150, height=200, name=DieHard) 26 | }, 27 | { 28 | id=654356453, 29 | title=Bad Boys, 30 | boxArt=BoxArt(width=150, height=200, name=BadBoys) 31 | }, 32 | { 33 | id=65432445, 34 | title=The Chamber, 35 | boxArt=BoxArt(width=150, height=200, name=TheChamber)}, 36 | { 37 | id=675465, 38 | title=Fracture, 39 | boxArt=BoxArt(width=150, height=200, name=Fracture) 40 | } 41 | ] 42 | */ 43 | ``` 44 | 45 | ## Conditions 46 | 47 | * Do not use any loops. 48 | 49 | ## Solution 50 | 51 |
Reveal

52 | 53 | --- 54 | ```kotlin 55 | fun getAllMoviesInformation(list: List): List> = 56 | list.flatMap { 57 | it.movies 58 | }.map { 59 | it.run { 60 | mapOf("id" to id, "title" to title, "boxArt" to boxArts.boxArt150()) 61 | } 62 | } 63 | 64 | private fun List.boxArt150() = firstOrNull { it.width == 150 } 65 | ``` 66 | 67 |

68 | 69 | --- 70 | 71 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Functional Hub 3 |

4 | 5 | # Functional Kotlin Katas 6 | 7 | ## List of katas 8 | 9 | ### 1 - Recursion 10 | 11 | #### - [Adding & Multiplying](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/recursion/adding/multiplying) 12 | 13 | #### - [Reduce](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/recursion/reduce) 14 | 15 | #### - [Fibonacci](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/recursion/fibonacci) 16 | 17 | ### 2 - Functions 18 | 19 | #### - [Currying](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functions/currying) 20 | 21 | #### - [Partial Application](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functions/partial) 22 | 23 | #### - [CPS](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functions/cps) 24 | 25 | ### 3 - Functional Transforms 26 | 27 | #### - [Map](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/map) 28 | 29 | #### - [Filter & Map](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/filtermap) 30 | 31 | #### - [FlatMap (Simple)](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/flatMap/simple) 32 | 33 | #### - [FlatMap (Advanced)](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/flatMap/advanced) 34 | 35 | #### - [Reduce](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/reduce) 36 | 37 | #### - [MapReduce](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/mapreduce) 38 | 39 | #### - [Zip](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/transforms/zip) 40 | 41 | ### 4 - Functor 42 | 43 | #### - [Creating the Try Functor](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/abstractions/functor) 44 | 45 | ### 5 - Monoid 46 | 47 | #### - [Adding Monoid to Try](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/abstractions/monoid) 48 | 49 | ### 6 - Monad 50 | 51 | #### - [Adding Monad to Try](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/abstractions/monad) 52 | 53 | ### 7 - Applicative 54 | 55 | #### - [Adding Applicative to Try](https://github.com/FunctionalKotlin/katas/tree/master/src/main/java/functional/abstractions/applicative) 56 | 57 | # How to test 58 | 59 | To test the different katas you can run the [unit tests provided](https://github.com/FunctionalKotlin/katas/blob/master/src/test/java/Tests.kt) inside the `test` folder. 60 | 61 | --- 62 | 63 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /src/main/java/functional/abstractions/functor/README.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | In functional programming we can use lots of different types of error handling and reporting, which allows us to choose the best strategy for out situation. 4 | 5 | - In one hand, we can use `Option` to model the absence of a value (like Kotlin's nullable) 6 | - Or, for example, `Either` (same as course's `Result`) if we want to model our result as a type either successful or failure. 7 | - On the other hand, we have `Try`, which represents an operation that can derive in a result (if it has been a success) or in an exception if something has failed. 8 | 9 | Taking this into account we can the infer that there are only two possible implementation for `Try`, a `Success` `Try` and a `Failure` `Try`. 10 | 11 | If we know that an operation could result in a failure, for example, because it is code from a library over which we have no control, or better yet, some method from the language itself. We can use Try as a substitute for the well-known try-catch, allowing us to rise to all its goodness. 12 | 13 | # Task 14 | 15 | Your task is to implement the `Try` type as a functor. 16 | 17 | For that, you need to create it as a sealed class and implement two children: `Success` and `Failure`. 18 | 19 | Also you need to be able to create `Try` instances out of the result of a lambda. 20 | 21 | Then you will need to create the following methods for it: 22 | 23 | - `filter`: It receives a predicate and returns a `Try` of the same type if the value contained matches the predicate. 24 | 25 | - `fold`: Receives a function for handling both cases of the `Try`. 26 | 27 | - `get`: Returns the value inside the `Try` or throws its error 28 | 29 | - `isFailure`: Returns true if the `Try` is a `Failure`. 30 | 31 | - `isSuccess`: Returns true if the `Try` is a `Success`. 32 | 33 | And, of course, you must implement the `map` function that will add a functor instance to this type. 34 | 35 | If you want to use the provided tests all this methods must be called as they are listed here and must be inside the `functional.abstractions.functor` package. 36 | 37 | ## Example 38 | 39 | ```kotlin 40 | Try { "Test".toInt() } 41 | .filter { it > 5 } 42 | .map { it + 2 } 43 | .fold({ 44 | exception -> "Error value" 45 | }, { 46 | value -> "The value is $value" 47 | }) // Error value 48 | 49 | Try { "4".toInt() } 50 | .filter { it > 5 } 51 | .map { it + 2 } // PredicateException("Predicate does not hold for 4") 52 | 53 | Try { "6".toInt() } 54 | .filter { it > 5 } 55 | .map { it + 2 } // Try.Success(8) 56 | ``` 57 | 58 | ## Solution 59 | 60 |
Reveal

61 | 62 | --- 63 | ```kotlin 64 | data class PredicateException(override val message: String) : Exception(message) 65 | 66 | typealias Failure = Try.Failure 67 | typealias Success = Try.Success 68 | 69 | sealed class Try { 70 | 71 | companion object { 72 | operator fun invoke(f: () -> A): Try = 73 | try { 74 | Success(f()) 75 | } catch (t: Throwable) { 76 | Failure(t) 77 | } 78 | } 79 | 80 | data class Success(val value: A): Try() 81 | data class Failure(val exception: Throwable): Try() 82 | 83 | } 84 | 85 | fun Try.filter(predicate: (A) -> Boolean): Try = 86 | fold({it -> Failure(it) }, {if (predicate(it)) Success(it) 87 | else Failure(PredicateException("Predicate does not hold for $it")) 88 | }) 89 | 90 | fun Try.fold( 91 | transformFailure: (Throwable) -> B, transformSuccess: (A) -> B): B = 92 | when(this) { 93 | is Failure -> transformFailure(exception) 94 | is Success -> try { 95 | transformSuccess(value) 96 | } catch (t: Throwable) { 97 | transformFailure(t) 98 | } 99 | } 100 | 101 | fun Try.get(): A = fold({ throw it }, { it }) 102 | 103 | fun Try.isFailure(): Boolean = this is Failure 104 | 105 | fun Try.isSuccess(): Boolean = this is Success 106 | 107 | fun Try.map(transform: (A) -> B): Try = 108 | fold({ Failure(it) }, { Success(transform(it)) }) 109 | ``` 110 | 111 |

112 | 113 | --- 114 | 115 | Copyright © [FunctionalHub.com](http://functionalhub.com) 2018. All rights reserved. -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/test/java/Tests.kt: -------------------------------------------------------------------------------- 1 | // Copyright © FunctionalHub.com 2018. All rights reserved. 2 | 3 | import functional.abstractions.applicative.ap 4 | import functional.abstractions.applicative.map 5 | import functional.transforms.castsDemo 6 | import functional.transforms.filtermap.getFiveRatingMoviesIds 7 | import functional.transforms.flatMap.advanced.getAllMoviesInformation 8 | import functional.transforms.flatMap.simple.getAllMoviesIds 9 | import functional.transforms.map.getMovies 10 | import functional.transforms.mapreduce.getUrlOfLargestBoxArt 11 | import functional.transforms.movieListsDemo 12 | import functional.transforms.moviesDemo 13 | import functional.transforms.reduce.getLargestRating 14 | import functional.transforms.zip.getFirstActorMap 15 | import functions.cps.FailureException 16 | import functions.cps.performOperationCPS 17 | import functions.currying.add 18 | import functions.currying.curried 19 | import functions.partial.Element 20 | import functions.partial.partial 21 | import functional.abstractions.functor.PredicateException 22 | import functional.abstractions.functor.Try 23 | import functional.abstractions.functor.filter 24 | import functional.abstractions.functor.fold 25 | import functional.abstractions.functor.get 26 | import functional.abstractions.functor.isFailure 27 | import functional.abstractions.functor.isSuccess 28 | import functional.abstractions.functor.map 29 | import functional.abstractions.monad.flatMap 30 | import functional.abstractions.monoid.plus 31 | import io.kotlintest.matchers.shouldBe 32 | import io.kotlintest.matchers.shouldThrow 33 | import io.kotlintest.specs.FreeSpec 34 | import recursion.adding.multiplying.canReach 35 | import recursion.fibonacci.fibonacci 36 | import recursion.reduce.reduceKt 37 | 38 | class Tests : FreeSpec() { 39 | init { 40 | "1 - Recursion" - { 41 | "ReduceKata" - { 42 | "reduceKt with String::plus should join all strings" { 43 | val list = listOf("functional", "hub", ".", "com") 44 | 45 | val reduced = list.reduceKt(String::plus) 46 | 47 | reduced shouldBe "functionalhub.com" 48 | } 49 | "reduceKt should calculate factorial" { 50 | val factorial = (1..10).toList().reduceKt(Int::times) 51 | 52 | factorial shouldBe 3628800 53 | } 54 | "reduceKt should fail if empty list" { 55 | shouldThrow { 56 | emptyList().reduceKt(String::plus) 57 | } 58 | } 59 | } 60 | "AddingMultiplyingKata" - { 61 | "canReach should work for certain combinations" { 62 | canReach(59, 5, 7) shouldBe true 63 | canReach(1, 5, 5) shouldBe true 64 | canReach(10, 5, 3) shouldBe false 65 | canReach(100, 1, 1) shouldBe true 66 | } 67 | "canReach should return same result as verified function" { 68 | fun testCanReach(n: Int, add: Int, multiply: Int): Boolean = 69 | if (n <= 1) { 70 | n == 1 71 | } else { 72 | if (n % multiply == 0 && multiply > 1) { 73 | testCanReach(n / multiply, add, multiply) || 74 | testCanReach(n - add, add, multiply) 75 | } else { 76 | testCanReach(n - add, add, multiply) 77 | } 78 | } 79 | 80 | (0..50).forEach { 81 | val n = (1..2000).random() 82 | val add = (1..20).random() 83 | val multiply = (1..20).random() 84 | 85 | canReach(n, add, multiply) shouldBe 86 | testCanReach(n, add, multiply) 87 | } 88 | 89 | } 90 | } 91 | "Fibonacci" - { 92 | "fibonacci should return fibonacci number in position" { 93 | val fibonacciList = listOf( 94 | 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 95 | 610, 987) 96 | 97 | fibonacciList.forEachIndexed { index, number -> 98 | fibonacci(index) shouldBe number 99 | } 100 | } 101 | } 102 | } 103 | "2 - Functions" - { 104 | "Currying" - { 105 | "curried function should curry any bi-function" { 106 | ::add.curried()(1)(3) shouldBe 4 107 | String::plus.curried()("He")("llo") shouldBe "Hello" 108 | } 109 | } 110 | "Partial" - { 111 | "partial must return a partially applied constructor" { 112 | partial(3) shouldBe Element("Blog", "Fixed", 3) 113 | partial(42) shouldBe Element("Blog", "Fixed", 42) 114 | } 115 | } 116 | "CPS" - { 117 | "performOperationCPS should call onSuccess if true" { 118 | var result = 0 119 | 120 | performOperationCPS( 121 | { true }, 122 | { int: Int -> result = int}, 123 | { throw AssertionError("Shouldn't be called") }) 124 | 125 | result shouldBe 42 126 | } 127 | "performOperationCPS should call onFailure if false" { 128 | var result = Exception() 129 | 130 | performOperationCPS( 131 | { false }, 132 | { throw AssertionError("Shouldn't be called")}, 133 | { ex: Exception -> result = ex }) 134 | 135 | result shouldBe FailureException("Error") 136 | } 137 | } 138 | } 139 | "3 - Functional transforms" - { 140 | "Map" - { 141 | "getMovies should return valid map" { 142 | getMovies(moviesDemo) shouldBe 143 | moviesDemo.map { it.id to it.title }.toMap() 144 | } 145 | } 146 | "Map & Filter" - { 147 | "getFiveRatingMoviesIds should return valid list" { 148 | getFiveRatingMoviesIds(moviesDemo) shouldBe moviesDemo.filter { 149 | it.rating == 5.0 150 | }.map { 151 | it.id 152 | } 153 | } 154 | } 155 | "FlatMap" - { 156 | "Simple" - { 157 | "getAllMoviesIds should return valid list" { 158 | getAllMoviesIds(movieListsDemo) shouldBe 159 | moviesDemo.map { it.id } 160 | } 161 | } 162 | "Advanced" - { 163 | "getAllMoviesInformation should return valid list" { 164 | getAllMoviesInformation(movieListsDemo) shouldBe 165 | moviesDemo.map { 166 | mapOf( 167 | "id" to it.id, "title" to it.title, 168 | "boxArt" to it.boxArts.boxArt150()) 169 | } 170 | } 171 | } 172 | } 173 | "Reduce" - { 174 | "getLargestRating should return largest rating" { 175 | getLargestRating(moviesDemo) shouldBe 5.0 176 | } 177 | } 178 | "MapReduce" - { 179 | "getUrlOfLargestBoxArt should return valid url" { 180 | getUrlOfLargestBoxArt(moviesDemo) shouldBe 181 | moviesDemo.flatMap { 182 | it.boxArts 183 | }.biggestBoxArt().url 184 | } 185 | } 186 | "Zip" - { 187 | "getFirstActorMap returns a map with the first actor names" { 188 | getFirstActorMap(moviesDemo, castsDemo) shouldBe moviesDemo 189 | .zip(castsDemo) 190 | .map { (movie, cast) -> 191 | movie.title to cast.nameOfFirstActor() 192 | }.toMap() 193 | } 194 | } 195 | } 196 | "4 - Functors" - { 197 | "Try" - { 198 | "Try companion should be able to be used as a function" { 199 | Try { "Test" }.get() shouldBe "Test" 200 | 201 | val exception = shouldThrow { 202 | Try { throwException() }.get() 203 | } 204 | 205 | exception.message shouldBe "Error" 206 | } 207 | "filter over Failure should return original exception" { 208 | shouldThrow { 209 | val aTry: Try = Try { "Test".toInt() } 210 | .filter { int: Int -> int > 5 } as Try 211 | 212 | aTry.get() 213 | } 214 | } 215 | "filter over Success should filter valid value" { 216 | val aTry: Try = Try { "6".toInt() }.filter { 217 | int: Int -> int > 5 218 | } as Try 219 | 220 | aTry.get() shouldBe 6 221 | } 222 | "filter over Success should fail if invalid value" { 223 | val exception = shouldThrow { 224 | val aTry: Try = Try { "2".toInt() } 225 | .filter { int: Int -> int > 5 } as Try 226 | 227 | aTry.get() 228 | } 229 | 230 | exception.message shouldBe "Predicate does not hold for 2" 231 | } 232 | "isSuccess should return false when Failure" { 233 | Try { throwException() } 234 | .isSuccess() shouldBe false 235 | } 236 | "isSuccess should return true when Success" { 237 | Try { "Test" }.isSuccess() shouldBe true 238 | } 239 | "isFailure should return true when Failure" { 240 | Try { throwException() } 241 | .isFailure() shouldBe true 242 | } 243 | "isFailure should return false when Success" { 244 | Try { "Test" }.isFailure() shouldBe false 245 | } 246 | "map should transform the value if Success" { 247 | val aTry: Try = Try { "Test" } 248 | .map { string: String -> "Valid $string" } 249 | as Try 250 | 251 | aTry.get() shouldBe "Valid Test" 252 | } 253 | "map should return original exception if Failure" { 254 | val exception = shouldThrow { 255 | val aTry: Try = Try { throwException() } 256 | .map { string: String -> "Valid $string" } 257 | as Try 258 | 259 | aTry.get() 260 | } 261 | 262 | exception.message shouldBe "Error" 263 | } 264 | "fold on success should return result from second parameter" { 265 | val result = Try { "4" } 266 | .fold({ 3 }, { string: String -> string.toInt() }) 267 | 268 | result shouldBe 4 269 | } 270 | "fold on failure should return result from first parameter" { 271 | val result = Try { throwException() } 272 | .fold({ 3 }, { string: String -> string.toInt() }) 273 | 274 | result shouldBe 3 275 | } 276 | } 277 | } 278 | "5 - Monoids" - { 279 | "Try" - { 280 | "plus should return failure if first Try is failure" { 281 | shouldThrow { 282 | val aTry: Try = Try { throwException() } 283 | .plus(Try { " Hi!" }) { 284 | a1: String, a2: String -> a1 + a2 285 | } as Try 286 | 287 | aTry.get() 288 | } 289 | } 290 | "plus should return failure if second Try is failure" { 291 | shouldThrow { 292 | val aTry: Try = Try { "Hello!" } 293 | .plus(Try { throwException() }) { 294 | a1: String, a2: String -> a1 + a2 295 | } as Try 296 | 297 | aTry.get() 298 | } 299 | } 300 | "plus should concat if both are success" { 301 | val aTry: Try = Try { "Hello!" } 302 | .plus(Try { " Hi!" }) { 303 | a1: String, a2: String -> a1 + a2 304 | } as Try 305 | 306 | aTry.get() shouldBe "Hello! Hi!" 307 | } 308 | } 309 | } 310 | "6 - Monads" - { 311 | "Try" - { 312 | "flatMap should transform if Success" { 313 | val aTry: Try = Try { "Test" }.flatMap { 314 | value: String -> Try { "A $value" } 315 | } as Try 316 | 317 | aTry.get() shouldBe "A Test" 318 | } 319 | "flatMap should fail if Failure" { 320 | val exception = shouldThrow { 321 | val aTry: Try = Try { "Test" }.flatMap { 322 | Try { throwException() } 323 | } as Try 324 | 325 | aTry.get() 326 | } 327 | 328 | exception.message shouldBe "Error" 329 | } 330 | } 331 | } 332 | "7 - Applicatives" - { 333 | "Try" - { 334 | "Apply parameter should work if all parameters are correct" { 335 | data class Class(val a: String, val b: Int) 336 | 337 | val value: Try = ((::Class.curried() map 338 | Try { "hello" }) as Try<(Int) -> Class> ap 339 | Try { 3 }) as Try 340 | 341 | value.get() shouldBe Class("hello", 3) 342 | } 343 | "Apply parameter should fail if any parameter is incorrect" { 344 | data class Class(val a: String, val b: Int) 345 | 346 | val exception = shouldThrow { 347 | val value: Try = ((::Class.curried() map 348 | Try { throwException() }) as Try<(Int) -> Class> ap 349 | Try { 3 }) as Try 350 | 351 | value.get() 352 | } 353 | 354 | exception.message shouldBe "Error" 355 | } 356 | } 357 | } 358 | } 359 | } 360 | --------------------------------------------------------------------------------
Reveal

30 | 31 | --- 32 | ```kotlin 33 | val List.split: Split? 34 | get() = takeIf { count() > 0 } 35 | ?.let { Split(this.first(), subList(1, count())) } 36 | 37 | data class Split(val head: T, val tail: List) 38 | 39 | tailrec fun List.fold(accumulator: B, operation: (B, A) -> B): B { 40 | val (head, tail) = split ?: return accumulator 41 | return tail.fold(operation(accumulator, head), operation) 42 | } 43 | 44 | fun List.reduceKt(operation: (acc: A, A) -> A): A = split?.let { 45 | (head, tail) -> tail.fold(head, operation) 46 | } ?: throw UnsupportedOperationException("value") 47 | ``` 48 | 49 |