`.
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 |

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 | 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 |
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
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 |
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 |
--------------------------------------------------------------------------------