initial commit
This commit is contained in:
commit
1cdf942f9d
46
Option.kt
Normal file
46
Option.kt
Normal file
@ -0,0 +1,46 @@
|
||||
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS", "UNCHECKED_CAST")
|
||||
inline class Option<A> private constructor(val value: Any?) {
|
||||
|
||||
inline fun <R> map(op: (A) -> R): Option<R> =
|
||||
if (this.value === EmptyOptionValue) {
|
||||
this as Option<R>
|
||||
} else {
|
||||
Option.just(op(this.value as A))
|
||||
}
|
||||
|
||||
inline fun filter(op: (A) -> Boolean): Option<A> =
|
||||
if (this.value === EmptyOptionValue || !op(this.value as A)) {
|
||||
none()
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
inline fun <R> fold(ifPresent: (A) -> R, ifEmpty: () -> R): R =
|
||||
if (this.value === EmptyOptionValue) {
|
||||
ifEmpty()
|
||||
} else {
|
||||
ifPresent(this.value as A)
|
||||
}
|
||||
|
||||
fun getOrNull(): A? =
|
||||
if (this.value === EmptyOptionValue) {
|
||||
null
|
||||
} else {
|
||||
this.value as A
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val EMPTY = Option<Any>(EmptyOptionValue)
|
||||
|
||||
fun <A> none() = EMPTY as Option<A>
|
||||
|
||||
fun <A> just(value: A) = Option<A>(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker for empty options.
|
||||
* This is necessary to allow nullable types in the option.
|
||||
* TODO: Somehow don’t expose this.
|
||||
*/
|
||||
object EmptyOptionValue
|
40
OptionTest.kt
Normal file
40
OptionTest.kt
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
|
||||
fun main() {
|
||||
testMap()
|
||||
testFilter()
|
||||
testFold()
|
||||
shouldHandleNullableTypes()
|
||||
}
|
||||
|
||||
fun testMap() {
|
||||
assertEquals(Option.just(3).map { it * 2 }.map { "$it" }.getOrNull(), "6")
|
||||
assertEquals(Option.just(listOf(1)).map { it.first() }.getOrNull(), 1)
|
||||
assertEquals(Option.none<Int>().map { error("Should not be executed") }.getOrNull(), null)
|
||||
}
|
||||
|
||||
fun testFilter() {
|
||||
assertEquals(Option.just(6).filter { it > 5 }.getOrNull(), 6)
|
||||
assertEquals(Option.just(6).filter { it < 5 }.getOrNull(), null)
|
||||
}
|
||||
|
||||
fun testFold() {
|
||||
assertEquals(Option.just(2).map { it * 2 }.fold({ it / 2 }, { error("Should not be executed") }), 2)
|
||||
assertEquals(Option.none<Int>().fold({ error("Should not be executed") }, { 2 }), 2)
|
||||
}
|
||||
|
||||
fun shouldHandleNullableTypes() {
|
||||
assertEquals(Option.just<Int?>(null).map { it ?: 5 }.getOrNull(), 5)
|
||||
assertEquals(
|
||||
Option.just<List<String>>(emptyList())
|
||||
.map { it.firstOrNull() }
|
||||
.fold({ it }, { error("Should not be executed") }),
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
private fun <T> assertEquals(x: T, y: T) {
|
||||
if (x != y) {
|
||||
error("$x was not equal to $y")
|
||||
}
|
||||
}
|
14
README.md
Normal file
14
README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# inline-option
|
||||
An implementation of an `Option<A>` type in Kotlin using the `inline class` feature.
|
||||
See https://kotlinlang.org/docs/reference/inline-classes.html
|
||||
|
||||
It’s just a small proof of concept which is why I decided to omit the usual gradle bloat.
|
||||
Build and test via:
|
||||
|
||||
```sh
|
||||
$ kotlinc-native Option.kt OptionTest.kt
|
||||
$ ./program.kexe
|
||||
```
|
||||
|
||||
The implementation uses an empty value placeholder to allow for nullable types (i.e. `Option<Int?>`).
|
||||
I don’t think those are ever a good idea to have, but I saw no reason not to support them.
|
Loading…
Reference in New Issue
Block a user