is there a function for `?:` similar to how there'...
# announcements
l
is there a function for
?:
similar to how there's
get()
for
[]
? if not, why not ?
b
No. [] Is syntactic sugar for operator function linked to a specific type, wheras ?: Is language level sugar for if(x ==null) (note how there's no function here)
c
you can probably use
getOrDefault
l
thats for optionals, elvis is for nullables
i figured i can just implement it myself
b
getOrDefault("key", "default value")
is the same as
get("key") ?: "defaultValue"
given
Map<String, String>
l
oh, so collections have it too. TIL. still can't use it on a nullable variable though
e
there's no function for
&&
||
either. those and
?:
do not evaluate the right hand side unless needed; that is fundamentally different than a function, where all arguments are evaluated before the call.
l
good point, is there a way i can adjust my implementation for that ?
nvm figured it out:
b
You can always write an inline function for custom syntactic sugar like that. Then instead f you having to be verbose about it in all usages, you only need to wrappit once and let compiler unwrap it in all call sites
l
i dont understand, can u make an example ?
b
Copy code
inline fun <T> T.elvis(fallback: () -> T) = this ?: fallback()

val x = null
val y = x ?: 42
val z = x.elvis { 42 }
It's just a matter of
inline
keywoard. Read on inline functions in kotlin docs 😉 Your case is one of the main use-cases for inline functions
l
i'm familiar with
inline
, i just dont see the point of using it here, nor how it makes the result any less verbose. technically you even made it more verbose by adding 1 more word
maybe you meant to add
inline
to my first implementation rather than my second, but i just tried that and it still evaluates the parameter in advance
b
The point is not that. It's a matter of optimisation. Without inline, you'll get a lambda function allocated for each call. With inline it just unwraps into call sites and the function itself disappears
So you get nicer api without any tradeoffs
l
ah i see, didn't know
inline
also unwraps the lambda parameter. but how does this make api nicer ? isnt this just a performance optimiuation ?
b
nicer as compared to not having this function at all and using
?:
l
now im confused
oh wait, i think i get it
b
I'm not comparing inline vs regular function
I'm talking about wrapper function vs just cofing in the check in all call sites
normally you have slight performance tradeoff between the two. With inline you don't
l
you meant that
getText().elvis{"no text"}.toUpper()
is nicer than
(getText() ?: "no text").toUpper()
right ?
b
From your perspective, yes. I personally prefer just using
?:
Basically if you MUST have
.elvis()
function, at least make it inline
l
alright got you. i agree that in this example
?:
is clear enough, but i think it becomes a bit messy in nested scenarios (which to be fair is quite the edge case) 1 more question: why doesnt inlining the first implementation do the trick? inlining basically means pasting the content of the function at the call site which would leave us with just
?:
which shouldn't evaluate the fallback unless needed, no ?
b
correct
it should work fine on first version of
.elvis()
too
l
well it doesnt
b
with inline?
l
yes
b
How do you decide it's not working?
l
Untitled.cpp
b
Works fine on playground: https://pl.kotl.in/qOlkxbQKw As you can see
run{}
is not getting evaluated
l
that doesnt mean the parameter passed to the function isnt evaluated
i edited my example to be more complete and readable, try that
b
Ah, right. No all the parameters passed in the function are resolved beforehand. Basically your execution flow is as follows: 1. Find that fn elvis need an argument of type Int 2. Evaluate fm f to get Int result (which is now compatible with inline 3. Unwrap inline function with that result repeated in all references within function body
Inline only unwraps its body, not the call site stuff like argument resolution
l
welp, guess i'll stick with the other solution then
b
You still gain benefits of inlining that other version of
elvis
l
yeah i see that now
for potential future readers: final version is this.
T : Any
is needed to guarantee that the lambda doesn't return null
inline
makes sure we don't loose any performance as it unwraps the lambda at compile time
b
you might even experiment with crossinline to unwrap argument lambda too
l
oh, i've never heard of that keyword. gotta look this up
ok i looked it up.
?:
allows using
return
as fallback, so
elvis
should too. therefore no
crossinline
needed
b
That wasn't the idea. I was thinking of getting rid of allocating mem for that fallback lambda and instead unwrapping it after ?: via crossinline
l
doesnt
inline
already do that ?
i thought that was the whole point of using
inline
in the first place
b
Honestly, I'm not sure anymore 😄 I thought inline just unwraps the function body, but not the arguments
l
crossinline
just prevents non-local returns
b
Hey, I've learned something new!
l
me too!