Skip to content

Instantly share code, notes, and snippets.

@roschlau
Last active August 16, 2018 23:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roschlau/c667e1e81e80ecd6fec46769c3f953b5 to your computer and use it in GitHub Desktop.
Save roschlau/c667e1e81e80ecd6fec46769c3f953b5 to your computer and use it in GitHub Desktop.
Implementing the defer, panic and recover functions from golang in Kotlin, because reasons. I have hardly any knowledge of Go, I googled a little bit about how those functions work. The example is copied from https://blog.golang.org/defer-panic-and-recover and results in exactly the same output.
import java.util.Optional
// Actual implementation
fun <T> deferringScope(block: DeferringScope<T>.() -> T): Optional<T> {
val scope = DeferringScope<T>()
try {
scope.result = Optional.of(scope.block())
} catch (e: Throwable) {
scope.error = e.message ?: e.toString()
}
val state = scope.exit()
return when(state) {
is ExitState.Normal<T> -> state.result
is ExitState.Panicked<T> -> throw Exception(state.error)
}
}
class DeferringScope<T> {
var result: Optional<T> = Optional.empty()
var error: String? = null
private val deferred = mutableListOf<DeferringScope<T>.() -> Unit>()
fun defer(block: DeferringScope<T>.() -> Unit) {
deferred.add(0, block)
}
fun panic(reason: String? = null) {
throw Exception(reason)
}
fun recover() = error.also { error = null }
fun exit(): ExitState<T> {
deferred.forEach { it() }
error?.let { error ->
return ExitState.Panicked(error)
}
return ExitState.Normal(result)
}
}
sealed class ExitState<T> {
class Normal<T>(val result: Optional<T>) : ExitState<T>()
class Panicked<T>(val error: String) : ExitState<T>()
}
import java.util.Optional
// Example usage, ported 1:1 from the last example in this blog: https://blog.golang.org/defer-panic-and-recover
fun main(args: Array<String>) { deferringScope<Unit> {
f()
println("Returned normally from f.")
}}
fun f() = deferringScope<Unit> {
defer {
recover()?.let { r ->
println("Recovered in f $r")
}
}
println("Calling g.")
g(0)
println("Returned normally from g.")
}
fun g(i: Int): Optional<Unit> = deferringScope {
if(i > 3) {
println("Panicking!")
panic(i.toString())
}
defer { println("Defer in g $i") }
println("Printing in g $i")
g(i + 1)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment