Skip to content

Commit

Permalink
✏️ Rename the function to runReader (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
kittinunf authored May 12, 2018
1 parent bb49e72 commit 75faf4e
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 30 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,28 @@
[![codecov](https://codecov.io/gh/mercari/ReaderK/branch/master/graph/badge.svg)](https://codecov.io/gh/mercari/ReaderK)

A Reader monad implemented in Kotlin

## Ideology

`Reader<In, Out>` is to provide higher abstraction of operation requires access to the external/shared environment in order to produce the desired output.

It helps you abstract away the computation into the very last moment (until `runReader` function is called). One of the benefit for Reader monad is to make the DI (dependency injection) easy, safe and straightforward.

## Installation

### Gradle

The latest version is [![jcenter](https://api.bintray.com/packages/mercari-inc/maven/ReaderK/images/download.svg)](https://bintray.com/mercari-inc/maven/ReaderK/_latestVersion)

``` Groovy
repositories {
jcenter()
}
dependencies {
compile 'com.mercari.readerk:readerk:<latest-version>'
}
```

### Explanation
5 changes: 2 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ buildscript {
}

dependencies {
classpath("com.android.tools.build:gradle:3.1.2")
classpath(kotlin("gradle-plugin", "1.2.41"))
classpath("com.novoda:bintray-release:0.8.0")
classpath(kotlin("gradle-plugin", extra.get("kotlinVersion") as String))
classpath("com.novoda:bintray-release:${extra.get("bintrayReleaseVersion")}")
}
}

Expand Down
5 changes: 5 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ org.gradle.jvmargs=-Xmx1536m
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
publishVersion=0.2.0
bintrayReleaseVersion=0.8.0

kotlinVersion=1.2.41

jUnitVersion=4.12

6 changes: 3 additions & 3 deletions readerk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ repositories {
}

dependencies {
implementation(kotlin("stdlib", "1.2.41"))
implementation(kotlin("stdlib", extra.get("kotlinVersion") as String))

testImplementation("junit:junit:4.12")
testImplementation("junit:junit:${extra.get("jUnitVersion")}")
}

jacoco {
Expand All @@ -35,7 +35,7 @@ configure<PublishExtension> {
uploadName = "ReaderK"
groupId = "com.mercari.readerk"
artifactId = "readerk"
publishVersion = "0.1.0"
publishVersion = extra.get("publishVersion") as String
autoPublish = true
desc = "A Reader monad implemented in Kotlin"
website = "https://github.com/mercari/ReaderK"
Expand Down
8 changes: 4 additions & 4 deletions readerk/src/main/java/com/merpay/readerk/Reader.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
package com.merpay.readerk

class Reader<T : Any, U : Any>(val read: (T) -> U) {
class Reader<T : Any, U : Any>(val runReader: (T) -> U) {

companion object {
//This is a wrapper over identity function
fun <T : Any> ask(): Reader<T, T> = Reader { t: T -> t }
}

fun <R : Any> local(f: (R) -> T): Reader<R, U> = Reader { r: R -> read(f(r)) }
fun <R : Any> local(f: (R) -> T): Reader<R, U> = Reader { r: R -> runReader(f(r)) }
}

//monad
fun <T : Any, Value : Any> Reader.Companion.pure(v: Value): Reader<T, Value> =
Reader { v }

fun <T : Any, U : Any, R : Any> Reader<T, U>.flatMap(transform: (U) -> Reader<T, R>): Reader<T, R> =
Reader { t: T -> transform(read(t)).read(t) }
Reader { t: T -> transform(runReader(t)).runReader(t) }

//functor
fun <T : Any, U : Any, R : Any> Reader<T, U>.map(transform: (U) -> R): Reader<T, R> = Reader { t: T -> transform(read(t)) }
fun <T : Any, U : Any, R : Any> Reader<T, U>.map(transform: (U) -> R): Reader<T, R> = Reader { t: T -> transform(runReader(t)) }

25 changes: 13 additions & 12 deletions readerk/src/test/java/com/merpay/readerk/DataStoreTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ class DataStoreTest {
fun set(key: String, value: String): Reader<DataStore, Unit> = Reader { store -> store.set(key, value) }
}

val memoryDataStore = MemoryDataStore()
private val memoryDataStore = MemoryDataStore()

val diskDataStore = DiskDataStore("src/test/assets")
private val diskDataStore = DiskDataStore("src/test/assets")

init {
diskDataStore.assetsDir.list { file, s -> file.delete() }
Expand All @@ -55,11 +55,11 @@ class DataStoreTest {
val getFoo1Reader = DataStoreReader.get("foo1")

//this reads(set value) into the memory data store
setReader.read(memoryDataStore)
setReader.runReader(memoryDataStore)

//read what we have set earlier
assertThat(getFoo1Reader.read(memoryDataStore), equalTo("hello world, reader"))
assertThat(DataStoreReader.get("xxx").read(memoryDataStore), equalTo(""))
//runReader what we have set earlier
assertThat(getFoo1Reader.runReader(memoryDataStore), equalTo("hello world, reader"))
assertThat(DataStoreReader.get("xxx").runReader(memoryDataStore), equalTo(""))
}

@Test
Expand All @@ -68,9 +68,9 @@ class DataStoreTest {
val getFoo1Reader = DataStoreReader.get("foo1")

//this reads(set value) into the disk data store
setReader.read(diskDataStore)
setReader.runReader(diskDataStore)

assertThat(getFoo1Reader.read(diskDataStore), equalTo("ReaderK is awesome"))
assertThat(getFoo1Reader.runReader(diskDataStore), equalTo("ReaderK is awesome"))
}

@Test
Expand All @@ -82,21 +82,22 @@ class DataStoreTest {

override fun set(key: String, value: String) {
//do nothing because this data store just return
println("set is called")
}

override fun get(key: String): String = "STATIC TEXT"
}

//use with local static data store
setBarReader.read(staticDataStore) //actually this doesn't matter, but yeah
assertThat(getBarReader.read(staticDataStore), equalTo("STATIC TEXT"))
setBarReader.runReader(staticDataStore) //actually this doesn't matter, but yeah
assertThat(getBarReader.runReader(staticDataStore), equalTo("STATIC TEXT"))

//use with disk data store
val value = setBarReader.flatMap { getBarReader }.map { it + it }.read(diskDataStore)
val value = setBarReader.flatMap { getBarReader }.map { it + it }.runReader(diskDataStore)
assertThat(value, equalTo("arstgkneio'arstgkneio'"))

//use with memory data store
val anotherValue = setBarReader.flatMap { getBarReader }.map { "simple value" }.read(memoryDataStore)
val anotherValue = setBarReader.flatMap { getBarReader }.map { "simple value" }.runReader(memoryDataStore)
assertThat(anotherValue, equalTo("simple value"))
}
}
16 changes: 8 additions & 8 deletions readerk/src/test/java/com/merpay/readerk/ReaderTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class ReaderTest {
fun pure() {
val just5Reader = Reader.pure<String, Int>(5)

assertThat(just5Reader.read("1234"), equalTo(5))
assertThat(just5Reader.runReader("1234"), equalTo(5))
}

@Test
Expand All @@ -19,14 +19,14 @@ class ReaderTest {

val intToCountLengthReader = countLengthReader.local { i: Int -> i.toString() }

assertThat(intToCountLengthReader.read(489), equalTo(3))
assertThat(intToCountLengthReader.runReader(489), equalTo(3))
}

@Test
fun ask() {
val identityReader = Reader.ask<Boolean>()

assertThat(identityReader.read(true), equalTo(true))
assertThat(identityReader.runReader(true), equalTo(true))
}

@Test
Expand All @@ -37,8 +37,8 @@ class ReaderTest {
//count the digit of absoluted value
val newReader = originalReader.map { it.toString().length }

assertThat(newReader.read(34), equalTo(2))
assertThat(newReader.read(-458), equalTo(3))
assertThat(newReader.runReader(34), equalTo(2))
assertThat(newReader.runReader(-458), equalTo(3))
}

@Test
Expand All @@ -49,17 +49,17 @@ class ReaderTest {
//map them to be the sum of all number
val newReader = originalReader.map { it.reduce { acc, item -> acc + item } }

assertThat(newReader.read(3 to 5), equalTo(15))
assertThat(newReader.runReader(3 to 5), equalTo(15))
}

@Test
fun flatMap() {
//identiy list of string reader
//identity list of string reader
val originalReader = Reader.ask<List<String>>()

//flatMap so it changes to join of all of items
val newReader = originalReader.flatMap { Reader { l: List<String> -> l.joinToString("&") } }

assertThat(newReader.read(listOf("1", "2", "3")), equalTo("1&2&3"))
assertThat(newReader.runReader(listOf("1", "2", "3")), equalTo("1&2&3"))
}
}

0 comments on commit 75faf4e

Please sign in to comment.