Last active
May 31, 2020 22:24
-
-
Save ajoz/f1c14da74501e9186e8d06b2edf8ec66 to your computer and use it in GitHub Desktop.
Refined types in Kotlin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Refined<A : Any>( | |
default: A, | |
private val errorMsgGen: (A) -> String = { | |
"Value: $it does not meet its constraints!" | |
}, | |
private val validator: (A) -> Boolean = { true } | |
) : ReadWriteProperty<Refined<A>?, A> { | |
private var backingField: A = default | |
override operator fun getValue(thisRef: Refined<A>?, property: KProperty<*>): A { | |
return backingField | |
} | |
override operator fun setValue(thisRef: Refined<A>?, property: KProperty<*>, value: A) { | |
if (validator(value)) { | |
backingField = value | |
} else { | |
throw IllegalStateException(errorMsgGen(value)) | |
} | |
} | |
} | |
// this will allow to enhance the error message with the typename | |
// a bit of experimentation with the `thisRef` showed that it | |
// always returns null | |
inline fun <reified A : Any> Refined( | |
default: A, | |
noinline validator: (A) -> Boolean | |
): Refined<A> { | |
return Refined( | |
default, | |
{ | |
"Value: $it does not meet type: ${A::class.java.simpleName} constraints!" | |
}, | |
validator | |
) | |
} | |
fun PositiveInt() = | |
Refined(0) { it > 0 } | |
fun NegativeInt() = | |
Refined(0) { it < 0 } | |
fun <A> NonEmptyList(vararg elements: A) = | |
Refined(elements.toList()) { it.isNotEmpty() } | |
fun main() { | |
// usage example | |
var positiveInteger: Int by NegativeInt() | |
positiveInteger = -1 | |
var nel: List<String> by NonEmptyList("this", "is", "a", "test") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment