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.Ruckus
09/25/2019, 10:49 PMTravis 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