Last active
May 25, 2018 15:06
-
-
Save apatrida/bde40641ebe53707afb65e4ba6249148 to your computer and use it in GitHub Desktop.
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
func submit() { | |
guard let name = nameField.text else { | |
show("No name to submit") | |
return | |
} | |
guard let address = addressField.text else { | |
show("No address to submit") | |
return | |
} | |
guard let phone = phoneField.text else { | |
show("No phone to submit") | |
return | |
} | |
sendToServer(name, address: address, phone: phone) | |
} | |
func sendToServer(name: String, address: String, phone: String) { | |
// ... | |
} |
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
// Guard-like extension function, maybe better called "assertNotNull" or it could be on an Optional instead of nullable | |
// note: this function returns a non null value of type T given a possibly null value of T? | |
// unless the value is null, in which case the lambda is invoked and it must return from the enclosing | |
// function or an exception is thrown preventing the null from affecting surrounding code. | |
i | |
inline fun <T: Any> T?.guard(onNullDo: ()->Unit): T { | |
if (this == null) { | |
onNullDo() | |
throw IllegalStateException("You must return from within the null guard block") | |
} | |
return this | |
} | |
// example using guard extension | |
fun submit() { | |
val name = nameField.text.guard { | |
show("No name to submit") | |
return // returns from submit() in case you are wondering, same as return@submit | |
} | |
val address = addressField.text.guard { | |
show("No address to submit") | |
return | |
} | |
val phone = phoneField.text.guard { | |
show("No phone to submit") | |
return | |
} | |
// name, address, and phone are all non-null at this point, guaranteed | |
sendToServer(name, address, phone) | |
} | |
fun sendToServer(name: String, address: String, phone: String) { | |
println("Sent $name, $address, $phone") | |
} |
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
// Guard-like inline function, maybe better called "assertNotNull" or it could be on an Optional instead of nullable | |
// note: this function returns a non null value of type T given a possibly null value of T? | |
// unless the value is null, in which case the lambda is invoked and it must return from the enclosing | |
// function or an exception is thrown preventing the null from affecting surrounding code. | |
inline fun <T: Any> guard(nullableThing: T?, onNullDo: ()->Unit): T { | |
if (nullableThing == null) { | |
onNullDo() | |
throw IllegalStateException("You must return from within the null guard block") | |
} | |
return nullableThing | |
} | |
fun submit() { | |
val name = guard(nameField.text) { | |
show("No name to submit") | |
return // returns from submit() in case you are wondering, same as return@submit | |
} | |
val address = guard(addressField.text) { | |
show("No address to submit") | |
return | |
} | |
val phone = guard(phoneField.text) { | |
show("No phone to submit") | |
return | |
} | |
// name, address, and phone are all non-null at this point, guaranteed | |
sendToServer(name, address, phone) | |
} | |
fun sendToServer(name: String, address: String, phone: String) { | |
println("Sent $name, $address, $phone") | |
} |
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
// Kotlin things that already act like guard. The `run` function could be changed to `let` or other similar function, | |
// or alias one of those to a function named `guard` | |
// | |
// 5 examples follow... | |
// === EXAMPLE 1: smart cast after null check | |
val something: String? = unknownResult() | |
if (something == null) { | |
return | |
} | |
// valid, because smart cast due to flow analysis shows something can never be null here | |
functionThatCannotAcceptNull(something) | |
// === EXAMPLE 2: Elvis operator taking action on null, smart cast due to return | |
val otherThing: String = unknownResult() ?: run { | |
reportError("bad bad bad!'") | |
return | |
} | |
// valid, because smart cast due to flow analysis shows otherThing can never be null here | |
functionThatCannotAcceptNull(otherThing) | |
// === EXAMPLE 3: Elvis operator with exception causes later smart cast | |
val lastThing: String = unknownResult() ?: throw IllegalStateException("bad data!") | |
// valid, because smart cast due to flow analysis shows lastThing can never be null here | |
functionThatCannotAcceptNull(lastThing) | |
// === EXMAPLE 4: make it look more guardy | |
val guardedThing: String = unknownResult<String>() guard { | |
reportError("bad bad bad") | |
return | |
} | |
// valid, because smart cast due to flow analysis shows guardedThing can never be null here | |
functionThatCannotAcceptNull(guardedThing) | |
// ... and this uses the following extension function marked infix just for fun (so space instead of "." to invoke) | |
inline infix fun <T: Any> T?.guard(onNullDo: ()->Unit): T { | |
if (this == null) { | |
onNullDo() | |
throw IllegalStateException("You must return from within the null guard block") | |
} | |
return this | |
} | |
// === EXMAPLE 5: make it look more guardy #2 but still use Elvis operator | |
val protectedThing: String = unknownResult() ?: guard { | |
reportError("bad bad bad") | |
return | |
} | |
// valid, because smart cast due to flow analysis shows protectedThing can never be null here | |
functionThatCannotAcceptNull(protectedThing) | |
// ... which requires this function basically aliasing `run` to `guard`: | |
inline fun <R> guardx(block: () -> R): R = run(block) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment