Skip to content

Instantly share code, notes, and snippets.

@Synesso
Last active October 1, 2019 21:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Synesso/38517eff8c3cc5eb6ab03a69a1a45335 to your computer and use it in GitHub Desktop.
Save Synesso/38517eff8c3cc5eb6ab03a69a1a45335 to your computer and use it in GitHub Desktop.
package ft.typesafety
import arrow.core.Option
import arrow.core.getOrElse
/**
* Use pattern matching and recursion. No vars, no loops, no overriding.
*
* `Option` is an implementation of optional functionality.
*
* We've made these exercises to give you greater insight into how an optional pattern
* might work in a functional language.
*
* When you see `Option` think: "It may exist, or it may not"
*
* There are two ways to construct an `Option`:
*
* `Some()` represents something that exists
*
* `None` represents something that doesn't exist
*
* We use `Option` in situations where there isn't certainty that a meaningful
* value will be returned to us.
*
*
* TODO - Rewrite these instructions for Kotlin
*
* The `get()` method on the key to value store `Map` is a great example of this.
*
* We expect `get()` to take a key and give us a value in return.
*
* But what happens when our Map doesn't know about the key we gave it?
*
* A Map here is the same as in any other language,
* we just need to tell it about the types we're working with.
*
* This is the type of the key
* |
* | This is the type of the value
* | |
* | |
* val myMap = mapOf<Int, String>( 1 to "one", 2 to "two", ...)
*
*
* When we call `get()` on Map we will always get back an `Option` type
*
* myMap.get(1) = Some("one") //The value exists and it's the string "one"
*
* myMap.get(0) = None //The value doesn't exist so we get None
*
* `Some("one")` and `None` are both of the type Option
*
* Since `Some` and `None` are the same type we can pattern match on them!
*
* We can have one set of logic when we get Some back and a different set
* of logic when we get `None` back!
*
* val mightBeSomething: Option<String> = myMap.get(3)
*
* val result: String = mightBeSomething match {
* case Some(string) => "I got a String back!"
* case None => "I got None back"
* }
*
* Good luck!
*
*/
object OptionalExercises1 {
val config = mapOf("host" to "squareup.com", "port" to "8080")
fun getFromConfig(key: String): Option<String> = Option.fromNullable(config[key])
fun lengthOfHost(): Option<Int> = getFromConfig("host").map { it.length }
fun portPlus1000(): Option<Int> = getFromConfig("port").map { it.toInt() + 1000 }
}
object OptionalExercises2 {
val hosts = mapOf("host1" to "squareup.com", "host2" to "test.squareup.com", "host3" to "netflix.com")
val envs = mapOf("squareup.com" to "prod", "test.squareup.com" to "test", "amazon.com" to "stage")
// Should return the env string if successful or "couldn't resolve" if unsuccessful
fun getEnvForHost(host: String): String = Option.fromNullable(hosts[host])
.flatMap { Option.fromNullable(envs[it]) }
.getOrElse { "couldn't resolve" }
// See how many ways you can implement this.
// Will either return "Connected to <squareup host>" or "not connected"
fun connectToSquareupHostsOnly(host: String): String = Option.fromNullable(hosts[host])
.filter { it.endsWith("squareup.com") }
.map { createConnection(it) }
.getOrElse { "not connected" }
private fun createConnection(domain: String): String = "connected to $domain"
}
/**
* Here we make the trait `Maybe`, which is our version of `Option`
*
* `Just` has the same behavior as `Some`
* `Nothing` has the same behavior as `None`
*
* We use this exercise to illustrate that we can create our own optional behavior
* with just a few functions.
*
*/
object OptionalExercises3 {
interface Maybe<out A>
data class Just<A>(val get: A) : Maybe<A>
object Nothing : Maybe<kotlin.Nothing>
fun <A, B> flatMap(m: Maybe<A>, f: (A) -> Maybe<B>): Maybe<B> = when(m) {
is Just -> f(m.get)
else -> Nothing
}
fun <A, B> map(m: Maybe<A>, f: (A) -> B): Maybe<B> = when(m) {
is Just -> Just(f(m.get))
else -> Nothing
}
fun <A, B> fold(m: Maybe<A>, default: () -> B, f: (A) -> B): B = when(m) {
is Just -> f(m.get)
else -> default()
}
fun <A> orElse(m: Maybe<A>, otherwise: () -> Maybe<A>): Maybe<A> = when(m) {
is Just -> m
else -> otherwise()
}
fun <A> orSome(m: Maybe<A>, default: () -> A): A = when(m) {
is Just -> m.get
else -> default()
}
fun <A, B, C> map2(m1: Maybe<A>, m2: Maybe<B>, f: (A, B) -> C): Maybe<C> =
flatMap(m1) { a -> map(m2) { b -> f(a, b) } }
fun <A> sequence(l: List<Maybe<A>>): Maybe<List<A>> = l.fold(Just(emptyList())) { acc, next ->
map2(acc, next) { xs, x -> xs.plus(x) }
}
fun <A, B> ap(m1: Maybe<A>, m2: Maybe<(A) -> B>): Maybe<B> = flatMap(m1) { a ->
map(m2) { f -> f(a) }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment