Is it possible to differentiate between a paramete...
# announcements
a
Is it possible to differentiate between a parameter being passed to a function and
null
in Kotlin?
Copy code
fun <T> aFunctionWithOptionalParam(
    optionalParam: T? = null
) {
    if (/* optionalParam is set*/) {
        someCallbackIfSet()
    }
}

fun whatIWant() {
    aFunctionWithOptionalParam(1) // callback is invoked
    aFunctionWithOptionalParam(null) // callback is invoked
    aFunctionWithOptionalParam() // callback should not be invoked
}
w
Can you give the default argument as
Nothing
?
🤦
😁 1
a
I think probably I could use java.util.Optional, but I’m doing Android so…
v
I don't think you can differentiate like that. You would need to wrap that parameter into something, so that
null
really means not set.
Actually
Optional
should never be used as parameter or field type, only as return type of methods
n
So I don't think this can be done conveniently in Kotlin because of the lack of sum type support
In python there are situations like this, where None is a valid value to be passed
The way it's done is that a stateless, implementation detail singleton is used as the default value
That way you can really tell if the user passed something unless they "reach in" and use this singleton, which isn't allowed
You could do something similar in Kotlin but making it fit in with the type system is hard
v
Besides that the last call does not actually compile as type info is missing 😄
The way to do it is to split the method into a version with one parameter and no default value and a version without parameter
Internally you can delegate to the same private method
n
Yeah overloading will work well as long as you only have one parameter like that. Internally it could pass a boolean to the common implementation
a
Yeah, my last resort would be that; I would have to copy the whole function basically.
v
Copy code
fun aFunctionWithOptionalParam() =
    aFunctionWithOptionalParamDelegate(null, false)
fun <T> aFunctionWithOptionalParam(optionalParam: T?) =
    aFunctionWithOptionalParamDelegate(optionalParam, true)

private fun <T> aFunctionWithOptionalParamDelegate(
    optionalParam: T?,
    paramGiven: Boolean 
) {
    if (paramGiven) {
        someCallbackIfSet()
    }
}
👆🏽 1
a
Yeah I tried something similar, but if I have nothing to pass then there is no T as well. I would have to do
Copy code
aFunctionWithOptionalParamDelegate<Any>()
or
<Nothing>
would be more approriate I think
a bit weird though
I should add that my goal is to reuse part of the function code instead of copying everything. I would probably have to make 4 functions to achieve the shape I want. I’ll share in a bit
v
Actually no, at least at play.kotl.in it works without type argument: https://pl.kotl.in/7FnfqxTH7
But yeah, if you need a type argument, just use
Nothing
a
Ohhhh, I thought it was same as I did, I didn’t notice
paramGiven
and the other function not having
<T>
. Nice
Anyway, I came up with this based on the overloading suggestion:
Copy code
fun <T> aFunctionWithOptionalParam(
    optionalParam: T
) {
    optionalParam?.run {
        someCallbackIfSet()
    }
    codeIDontWantToDuplicate()
}

fun aFunctionWithOptionalParam() {
    codeIDontWantToDuplicate()
}

fun codeIDontWantToDuplicate(
) {}

fun codeIDontWantToDuplicate() {}

fun whatIWant() {
    aFunctionWithOptionalParam(optionalParam = 1) // code is run
    aFunctionWithOptionalParam(optionalParam = null) // code is run
    aFunctionWithOptionalParam() // optional code is not run
}
v
If the thing to do with the optional param is a strict prefix before the common code, you can of course also do it like that
Or you do the
codeIDontWantToDuplicate
stuff directly within
aFunctionWithOptionalParam()
and then call
aFunctionWithOptionalParam()
from within
aFunctionWithOptionalParam(T)
after doing the optional action, then you don't need the extra
codeIDontWantToDuplicate()
a
Right, that’s even better. I guess I overthought I little bit here
Huge thanks to everyone for your input!
v
yw
b
You can make optional parameter
Any?
Instead of
T?
then as a default value you can use
NULL = Any()
One drawback here that you will loose compile time check that optional param is
T?
v
Which would only be relevant if T is used for anything else, for example return type. But as it does not seem to be, this is maybe the most elegant solution actually 🙂
a
ohhh. good ideas keep coming. In my case the idea is I'll be passing T to the callback only uf its set. So I need T in this case. This was not apparent from my sample code above.
b
You can still do cast:
callback.invoke(param as T?)
a
yeah, but the main purpose of the function I wrote is to introduce typesafety for something unsafe. 😅
your solution could be useful for me i. the future however. keeping it in mind
c
@Nir Python's
None
is Kotlin's
Unit
, but without union types that doesn't really solve your issue (however the Kotlin Arrow library has union types but I don't think it's stable yet?)
n
How do you figure that None is Unit?
c
It's a singleton that represents ‘no value', it's the same concept
n
And null is a singleton too
There's lots of stateless singletons
At any rate the whole point of the pattern I mentioned is that the singleton has to be private. You can't use Unit.
t
What about:
Copy code
fun <T> aFunctionWithOptionalParam(
    optionalParam: T? = null
) {
    funInternal(NullableOptional.Value(optionalParam))
}

fun aFunctionWithOptionalParam() {
    funInternal(NullableOptional.Empty)
}

private sealed class NullableOptional<T> {
    data class Value<T>(val value: T) : NullableOptional<T>()
    object Empty : NullableOptional<Nothing>()
}

private fun <T> funInternal(optional: NullableOptional<T>) {
    if (optional is NullableOptional.Value) {
        if (optional.value != null) {
            // handle value
        } else {
            // handle null
        }
    } else {
        // handle no value
    }
}
a
I had to come back to this for a different use case…
Copy code
val hasSavedValue = contains(key) // returns true even if null (value set)
val savedOrInitialValue: T = if (hasSavedValue) {
    get(key)!! // returns `T?`
    // will above work the same as `requireNotNull(get(key))` ?
} else { initialValue }
This time I always know there is an initialValue, but I don’t want to use initialValue if there is a saved value, even if the saved value is null. Is this the right way to do it? Or will
!!
throw an exception for any null (even if it’s expected)?
n
This is like in the contest of a
Map
right?
Couldn't you just use
getOrDefault
?
v
get(key)!!
will throw an exception if
get(key)
returned
null
.
requireNotNull(key)
will throw an exception if
key
is
null
.
a
Couldn’t you just use 
getOrDefault
 ?
Ah… sh*t. It’s a Map but there a framework class wrapper on top of the it… I wish I could just use it
Copy code
@MainThread
    @Nullable
    public <T> T get(@NonNull String key) {
        return (T) mMap.get(key);
    }
@Vampire Oops, that’s a typo, I meant
requireNotNull(get(key))
v
Then it is the same, both will throw an exception, just different ones
What would you want to happen if there is
null
in the map for the key?
n
I think if you cannot use getOrDefault then your code is fine
a
I want the null if it is set to the key, not the initial value…
n
Oh, I see the problem
v
Why the
!!
then?
a
for example the initial state can be non null but dev can set it to null later (for whatever reason, I’m building a util function)
n
Out of curiosity is this generic code Allan?
a
yes.
v
if (containsKey(key)) get(key) else initialValue
n
that's not correct
if the mapped to type T, that expression returns T?
Kotlin makes working with null very awkward in the presence of generic code. The problem is that in Kotlin, multiple levels of null get collapsed
a
@Vampire yeah removing that null check gives me this
v
Of course it does
n
If you look at how Optional is handled in most languages, this collapsing does not happen. In say C++, if map<K, V>::get returned an Optional<V>, then a map<K, Optional<int>> would return Optional<Optional<int>>
v
Ah, and your T is actually a nullable type then?
n
And then you could remove one level of Optional and be fine. In Kotlin, get returns V? whether you have a Map<K, V>, or a Map<K, V?>
and that's arguably kind of wrong, or strange
I think your best bet here is probably getValue
a
theres no restriction, so yeah. Or if I could force T to be non-nullable type, I could work with that as wel
n
yeah, if you use getValue(key) instead of get(key)!! it works out
basically in Kotlin, if you want to write code like this that's correct over a generic T that could be nullable, you have to avoid using any API that contains things like T?
getValue(key) returns exactly a V, without adding any null to it so it will be exactly the correct type
Copy code
val hasSavedValue = contains(key)
val savedOrInitialValue: T = if (hasSavedValue) getValue(key) else initialValue
a
yeah i can't use other map methods since its wrapped ;(
n
yeah, the code above should work
a
just had an idea since actually it's not supposed to accept null anywhere
I guess I just throw an exception if aomething is null
force users not to use a nullable Type for T
n
Yeah, I mean obviously if you can constrain it so that you are working with a Map<K, V> where V: Any
then it's a lot easier
a
I'm actually using this for MutableStateFlow. which requires initial value
n
technically it does not look like MutableStateFlow<T> is constrained at all
So youc ould have MutableStateFlow<Int?>
a
yep thats why
I'd rather crash nulls than a different behavior when accepting nulls
ok at least I got a last resort
n
Yeah, this makes me realize how important getValue is
a
The culprit here is the
@Nullable
on the wrapper
get
function. -_-, would have worked otherwise.
anyway, thank you both for your inputs