```val myAnnotation = maybeAnnotatedClass.getAnnot...
# getting-started
m
Copy code
val myAnnotation = maybeAnnotatedClass.getAnnotationByClassId(
    classId = ANNOTATION_CLASS,
    session = session,
)
    ?: return null
    .getKClassArgument(ANNOTATION_ARG)!!
    .classId!!
Is there a way to mark the end of the Elvis operand? The code above is rejected because the operator maximally munches the rest of the expression
Copy code
val myAnnotation = maybeAnnotatedClass.getAnnotationByClassId(
    classId = ANNOTATION_CLASS,
    session = session,
)
    .let { nullable -> nullable ?: return null }
    .getKClassArgument(ANNOTATION_ARG)!!
    .classId!!
I constantly have to do this, which would have been fine if I could abstract out the
.let { nullable -> nullable ?: return null }
into its own extension function that I could reuse, but I cannot. I don’t want to wrap the whole function body in an Arrow
nullable
just so that I could call
nullable.bind()
either
Copy code
val myAnnotation = maybeAnnotatedClass.getAnnotationByClassId(
    classId = ANNOTATION_CLASS,
    session = session,
)
    ?.getKClassArgument(ANNOTATION_ARG)
    ?.classId
    ?: return null
Also, please do not suggest alternatives such as this. I need to return early from the nullable (fallible) function if a given class is not annotated with the
annotation class
for my plugin (a failure mode). If it is annotated however but lacks a
KClass
argument, something inexplicable has gone profoundly wrong and there’s no recovery path for it which merits the use of
!!
p
Either
.let { it ?: return null}
or
(expr ?: return null).restOfChain()
m
To my mind, considering different failure / recover modes for this chain call, I'd very much prefer to see two separate expressions in two separate variables here.
plus1 4
m
@Marat Akhin What do you mean?
m
Copy code
val classAnno = maybeAnnotatedClass.getAnnotationByClassId(
    classId = ANNOTATION_CLASS,
    session = session,
) ?: return null
    
val classAnnoArgument = classAnno
    .getKClassArgument(ANNOTATION_ARG)!!
    .classId!!
m
The only thing that that separation achieves is overcoming the syntactic limit imposed by the maximal Elvis operator.
classAnno
is private to the call chain in the sense that it’s only the business of the chain and of nothing else. Why should it even be visible to the entire rest of the function? That’s one more binding for the reader of the function to keep track of in their head.
p
You could also argue having a return hidden deep in the middle of a long chain of operations is just as harmful for the reader. Separation clarifies the stages and highlights the return. If you're worried about the scope of the function, extract a helper.
➕ 2
m
When you "hide" the implicit contract of
I need to return early from the nullable (fallible) function if a given class is not annotated with the
annotation class
for my plugin (a failure mode). If it is annotated however but lacks a
KClass
argument, something inexplicable has gone profoundly wrong and there’s no recovery path for it which merits the use of
!!
.
inside a call chain, to my mind, that's one more thing the reader needs to (now) carefully read and still track. I'd trade that for one more variable.
➕ 1
m
People can have opinions about this particular example, and that is their right. But the more important thing is that this friction point keeps coming up in call chains. Is there a way to mark the end of an Elvis operand?
m
I believe the most accurate description of the friction between you and Kotlin is: your code style is not exactly the code style Kotlin wants to encourage its users to write.
😅 1
➕ 2
p
Yes. By encapsulating the entire elvis expression in parentheses.
☝️ 1
☝🏻 1
m
Copy code
inline fun <T: Any> T?.elvis(f: () -> T): T {
    return this ?: f()
}
I think I could also declare an extension like this
p
If it eases your style, you could introduce an
inline fun <T: Any> T?.ifNull(f: () -> T): T
helper so you can
.ifNull { return null }
m
Copy code
val myAnnotation = maybeAnnotatedClass.getAnnotationByClassId(
    classId = ANNOTATION_CLASS,
    session = session,
)
    .elvis { return null }
    .getKClassArgument(ANNOTATION_ARG)!!
    .classId!!
@phldavies I could live with this, thank you!
p
I'd keep to standard stdlib naming style for it though (
ifNull
) although personally I'd keep to
.let { it ?: return null }
m
.let { it ?: return null }
is way too verbose given its frequency. But you make a good point about
ifNull
, thank you for your advice