Skip to content

Commit

Permalink
Adding FP to the max implementation (#987)
Browse files Browse the repository at this point in the history
* Adding an implementation of FP to the Max as an arrow example

* Adding an implementation of FP to the Max as an arrow example

Renaming a few things
  • Loading branch information
enhan authored and pakoito committed Aug 14, 2018
1 parent ce41e46 commit 7839164
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -709,3 +709,7 @@ Some of the mentioned concepts like purity are described in the following blogpo

* [Kotlin Functional Programming: Does it make sense?](https://medium.com/@JorgeCastilloPr/kotlin-functional-programming-does-it-make-sense-36ad07e6bacf) by [Jorge Castillo](https://www.twitter.com/JorgeCastilloPR))
* [Kotlin purity and Function Memoization](https://medium.com/@JorgeCastilloPr/kotlin-purity-and-function-memoization-b12ab35d70a5) by [Jorge Castillo](https://www.twitter.com/JorgeCastilloPR))

Also, consider watching [FP to the max](https://youtu.be/sxudIMiOo68) by [John De Goes](https://twitter.com/jdegoes) and
the related `FpToTheMax.kt` example located in the `arrow-examples` module. This technique may seem to be a huge overhead
on such a small example, but it is meant to be used at scale.
24 changes: 24 additions & 0 deletions modules/docs/arrow-examples/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@

apply plugin: 'idea'

idea {
module {
sourceDirs += files(
'build/generated/source/kapt/main',
'build/generated/source/kapt/debug',
'build/generated/source/kapt/release',
'build/generated/source/kaptKotlin/main',
'build/generated/source/kaptKotlin/debug',
'build/generated/source/kaptKotlin/release',
'build/tmp/kapt/main/kotlinGenerated')
generatedSourceDirs += files(
'build/generated/source/kapt/main',
'build/generated/source/kapt/debug',
'build/generated/source/kapt/release',
'build/generated/source/kaptKotlin/main',
'build/generated/source/kaptKotlin/debug',
'build/generated/source/kaptKotlin/release',
'build/tmp/kapt/main/kotlinGenerated')
}
}

dependencies {
compile project(':arrow-data')
compile project(':arrow-effects')
Expand Down
89 changes: 89 additions & 0 deletions modules/docs/arrow-examples/src/test/kotlin/arrow/FpToTheMax.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package arrow

import arrow.core.Option
import arrow.core.Try
import arrow.effects.ForIO
import arrow.effects.IO
import arrow.effects.console
import arrow.effects.fRandom
import arrow.effects.fix
import arrow.effects.monad
import arrow.typeclasses.Monad
import arrow.typeclasses.binding
import java.util.Random

/**
* This sample is a simple translation in Kotlin (using arrow, of course) of this talk: https://youtu.be/sxudIMiOo68
*/


object ORandom : Random()

interface Console<F> {
fun putStrLn(s: String): Kind<F, Unit>
fun getStrLn(): Kind<F, String>
}

@instance(IO::class)
interface IOConsoleInstance : Console<ForIO> {

override fun putStrLn(s: String): Kind<ForIO, Unit> = IO { println(s) }

override fun getStrLn(): Kind<ForIO, String> = IO { readLine().orEmpty() }

}

interface FRandom<F> {
fun nextInt(upper: Int): Kind<F, Int>
}

@instance(IO::class)
interface FRandomInstance: FRandom<ForIO> {
override fun nextInt(upper: Int): Kind<ForIO, Int> = IO { ORandom.nextInt(upper) }
}

class MonadAndConsoleRandom<F>(M: Monad<F>, C: Console<F>, R: FRandom<F>): Monad<F> by M, Console<F> by C, FRandom<F> by R {

}

object FpToTheMax {

fun parseInt(s: String): Option<Int> = Try { s.toInt() }.toOption()


fun <F> checkContinue(MC: MonadAndConsoleRandom<F>, name: String): Kind<F,Boolean> = MC.binding {
MC.putStrLn("Do you want to continue, $name?").bind()
val input = MC.getStrLn().map { it.toLowerCase() }.bind()
when (input) {
"y" -> MC.just(true)
"n" -> MC.just(false)
else -> checkContinue(MC, name)
}.bind()
}

fun <F> gameLoop(MC: MonadAndConsoleRandom<F>, name: String): Kind<F, Unit> = MC.binding {
val num = MC.nextInt(5).map { it + 1 }.bind()
MC.putStrLn("Dear $name, please guess a number from 1 to 5:").bind()
val input = MC.getStrLn().bind()
parseInt(input).fold({ MC.putStrLn("You did not enter a number")}){ guess ->
if (guess == num) MC.putStrLn("You guessed right, $name!")
else MC.putStrLn("You guessed wrong, $name! The number was: $num")
}.bind()
val cont = checkContinue(MC, name).bind()
(if (cont) gameLoop(MC, name) else MC.just(Unit)).bind()
}

fun <F> fMain(MC: MonadAndConsoleRandom<F>): Kind<F, Unit> = MC.binding {
MC.putStrLn("What is your name?").bind()
val name = MC.getStrLn().bind()
MC.putStrLn("Hello $name, welcome to the game").bind()
gameLoop(MC, name).bind()
}

@JvmStatic
fun main(args: Array<String>) {
val r = fMain(MonadAndConsoleRandom(IO.monad(), IO.console(), IO.fRandom()))
r.fix().unsafeRunSync()
}

}

0 comments on commit 7839164

Please sign in to comment.