Travis Griggs
09/25/2019, 10:02 PM(some.code)?.let { thing ->
makeHayWith(thing)
} ?: run {
mournNoThing()
}
(it follows Swift’s if let … {} else {} pattern nicely)
But I discovered there’s a hole in this that burned us. I should have seen this and feel kind of silly since I did Smalltalk with ubiqutuous block closures for all control flow for years. The let/run pattern breaks down when you do this:
(some.code)?.let { thing ->
makeHayWith(thing)?.let { hay ->
bragAboutMy(hay)
}
} ?: run {
mournNoThing()
}
If makeHayWith returns a null, then the return value of the original .let block will return null, which causes the run block to run in addition to the outer let block. So what you thought was a nice substitute for Swift’s if let ... else pattern has just been turned into a bug waiting to happen.
I’m aware of the “smart pointers” described in https://kotlinlang.org/docs/reference/null-safety.html, but these don’t really appeal to me. I like something a little more intentional and universally applied.
I rarely use the kotlin let pattern as an expression, almost always as a statement, so I toyed with writing my own variant that returned a distinct type (rather than the closure return type) so that I could through sugary magic do something more akin to the Swift let thing. But I’m not sure I want to stray that far from “idiomatic Kotlin”
I guess my question is, is there another pattern that I’m not aware of that I should be considering using?karelpeeters
09/25/2019, 10:04 PMif and smartcasting is often the better solution.Travis Griggs
09/25/2019, 11:39 PMKroppeb
09/26/2019, 12:14 AM.also{} would also work but if is betterTravis Griggs
09/26/2019, 12:36 AMKroppeb
09/26/2019, 2:15 AMMark McCormick
09/26/2019, 8:50 AM.apply / run since apply returns the initial value not what the block resolves toAlowaniak
09/26/2019, 9:23 AM