Is there a nicer way to write ```val lambda = if (...
# getting-started
m
Is there a nicer way to write
Copy code
val lambda = if (myClickHandler == null) null else {
    myClickHandler::onClick
}
I wish this was possible:
Copy code
myClickHandler?::onClick
j
Not sure if much nicer, but it's quite common to replace such
if
statements with:
Copy code
val lambda = myClickHandler?.let { it::onclick }
In general if the expression in the non-null case takes the object you're testing as receiver, you could just use
?
, as in
thing?.propertyOrFun
. But if the object you're testing is used as something else in the non-null case (not a simple receiver), you can always use
let
as shown above. Whether it's more readable than a plain
if
depends on the expression IMO. You can see
?let { .. }
on a nullable value like a
map
call on an Optional.
m
I was hoping at least I could do something like this:
Copy code
myClickHandler?.let(MyClickHandler::onClick)
Regardless, it’s a shame this is not possible:
Copy code
myClickHandler?::onClick
Not sure if it’s worth doing (because your first solution reads simpler) but there is this:
Copy code
myClickHandler?.toLambda(MyClickHandler::onClick)

fun <T> MyClickHandler.toLambda(lambda: MyClickHandler.(T) -> Unit): (T) -> Unit =
    { this.lambda(it) }
but I really want to have a more generic type for
lambda
.
j
Your first example doesn't work because
let
expects a function that transforms its argument (the receiver of
let
) into whatever you want as return value of the whole
let
call.
MyClickHandler::onClick
is a function that takes an instance of
MyClickHandler
AND whatever input
onClick
takes, and returns the result of
onClick
. This is quite different. If it compiled, you wouldn't get a function out of that
let(MyClickHandler::onClick)
call, but a simple value that's the return value of
onClick
👍 1
Your attempt with
toLambda
could be interesting in general, but I think in this case it just adds more complexity. If the contributors are familiar with Kotlin and
let
, the expression with
let
has less cognitive overhead IMO
Also, the naming
toLambda
is a bit strange, as there is no lambda in the caller's syntax in the end. You just get an instance of a function type, while a lambda would be a
{ .. }
literal.
m
Not sure I understand your last point. The return value is a lambda, or maybe I have the wrong understanding of the term lambda!
j
I think it's just a matter of terminology here. Instances of functions are not called lambdas in general. They are just instances of function types. From the Kotlin doc, a lambda expression is a function literal, a way to describe the body of a function without a name in the source code. It's not a type of value. The type of such value is a function type.
m
Okay, that makes sense. Although here I’m creating a new function literal - i.e. not some existing function reference. EDIT: talking about return value, not arg
j
Yep there is a lambda in the implementation of
toLambda
, but from the caller's point of view there is no lambda (it could have been implemented with an anonymous function and still yield the same function result)
m
Right, it’s just an implementation detail. So what is a good terminology for this?
toNotNullFunction(nullableFunction: ...)
?
j
I honestly don't have a great alternative name for this, sorry (I know it's unfair to point out the problems in a name and not have any alternative 😄 ). Usually you would avoid the whole need for conversions by using function types or functional interfaces, and/or SAM conversions.
Why do you need such conversion in the first place? Do you own the click handler type?
m
Yes, I’ve created a click handler with a bunch of onXClick() functions. I generally pass around a nullable click handler at the higher level, but lower level code will only need a single nullable function on the click handler (of course, the lower-level code does not know (or need to know) about the higher level handler).
j
Ah ok, if there are several functions there, you can't really use SAM conversions / functional interfaces, got it. Not sure if the nullability is to enable doing more than just ignore the handler, but if not, you could just have a no-op implementation of the general handler with lots of methods, and have it non-nullable everywhere. Of course this depends on your use case
m
I use the null to indicate that clicks are not handled. This reduces the work required by lower-level code - e.g. (Android) no need to create clickable spans. I know it’s slightly hacky, and I should really be using sealed classes etc, but…
j
I wouldn't say it's that hacky, it seems decent to me to represent "no handler" as a null handler function. Thanks for sharing your use case, anyway. Then I guess I would go for my initial suggestion with
?.let { it::onClick }
when you need to pass a single function from the bigger nullable multi-function handler instance.
m
Yeah, it’s definitely the best solution. Thanks very much for your help.
j
Hey I just thought of another option. Not sure to which extent it's acceptable, but you could also define an interface for each of the single-method handlers you need in lower-level components, and make your big multi-function handler type implement all of those, so you could just directly pass it on.
m
Yeah I thought about that too, but I don’t really want to have so many single function interfaces.
👍 1
y
just a suggestion,
bindReceiver
would probably be a good name for the
toLambda
function because that's really what it's doing, it binds your
myClickHandler
to the passed-in function, and it is just that the
bindReceiver
function happens conditionally
👍 1