I don't really use let, any, apply, etc like this ...
# getting-started
c
I don't really use let, any, apply, etc like this says https://twitter.com/danlew42/status/1095342932012412928 I came across this today
Copy code
val userAddress = user?. let { user → getAddressByUserName(user) } ?: "None"
Why would you use let instead of just
Copy code
val userAddress = getAddressByUserName(user ?: "None")
or just an if statement? Maybe
let
just doesn't make sense to me since english isn't my first language. why
let
?
y
let
is quite mathematical I guess, definitely not colloquial English. The example you gave assumes that
getAddressByUserName
can handle the input
"None"
👍 1
c
mathematical? hm. let me look up the math definition of it! thanks!
s
but the way we use the function that name is applied to is somewhat analogous to
fmap
on monads in purer functional languages, without the need to construct a monad around any of our data
it still sorta fits the original bill—within the lambda you pass to
let
, you basically have a new scope that takes in the value you're calling
let
on, and defines
it
or another name within that scope with said value. It also allows you to generically return another type, likely the result of transforming the incoming value
I use it a lot for railway-oriented programming and, of course, for
?.let {}
, one use for which is to let me safely compose functions in sequence that might return null
h
BTW your replacement is not the same. Because if statements are expressions in Kotlin, I/we at work try to minimize the usage of the scope functions and prefer to use if/when or just more variables because it makes the code more readable (we think). Especially when you do a code review without an IDE. Same for forEach vs for loops. Also, the debugger does not jump into a scope function by default, making debugging annoying. But at the end, it’s just a matter of style, procedural vs functional.
‼️ 2
t
Copy code
val userAddress = getAddressByUserName(user ?: "None")
isnt exactly the same, now the method
getAddressByUserName
needs to know of the "None" condition and act accordingly (which is not a good pattern). But using an if statement is indeed generally the same thing (since it is also an expression),
let
is the more concise and doesnt break the flow as much
1
c
Copy code
val userAddress = user
    ?.let { user -> getAddressByUserName(user) } 
    ?: "None"
is equivalent to
Copy code
val userAddress =
    if (user != null) getAddressByUserName(user) ?: "None"
    else "None"
I avoid
?.let {} ?:
because it's almost always ambiguous to know which cases may return the default value (it's both the initial value and the function's result). I have seen this pattern be the cause of bugs way too many times. I only use
.let
(not
?.let
), and even then, I forbid myself from using anything more complex than a single function call, preferably written as a function reference.
m
If
user
is a
var
property of a class, then the
if
statement doesn't work because the smart cast doesn't work. The
let
call allows you to avoid creating a new local variable at the scope above the
let
.
👍 1
r
If user is a var property of a class,
Or a
val
declared in a different module.
k
I avoid
?.let {} ?:
because it's almost always ambiguous to know which cases may return the default value
It's not just with
let
but this ambiguity is present with most uses of
?.
. For example:
Copy code
val x = foo()?.bar() ?: throw SomeException()
This will throw if either
foo()
or
bar()
returns null. Sometimes, it's only
foo()
that is not allowed to return null, and it's ok for
bar()
to do so. The above can be corrected to this:
Copy code
val x = (foo() ?: throw SomeException()).bar()
1