This is more thinking out loud than a language pro...
# language-proposals
d
This is more thinking out loud than a language proposal... Default arguments are great. But let's compare them with a builder pattern for a second:
Copy code
Something.Builder()
   .apply {
     foo("mandatory argument*)
     val barArg = grabBarArg()
     if (barArg != null) {
        bar(barArg)
     } else {
        // do nothing, leave the default
     }
   }
   .build()
With the default parameters we don't have the option of leaving the default like that;
Copy code
Something (
    foo = "mandatory argument",
    bar = grabBarArg() ?: _
)
I think it would be helpful to have a way of explicitly saying "use default" like passing a
Nothing
? Or in some other similar way? When I'm this situation if i have control on the class i create a secondary constructor, but it could be helpful, i think, to have a way to provide the default from the caller. It might also improve kotlin / java interoperability.
10
d
oh, and it is explained far better than what i did, of course, thanks
f
Hmmm... I'm unsure about how this works with APIs and ABIs in regards to changes to default values. The default way for this is to expose a nullable parameter and hide the actual default value. This ensures two things 1. the default is hidden from the public API and ABI 2. you may pass null to get the default. Seems like a proper solution here too, no?
e
what if null is a valid value?
f
That's of course a problem, I guess
Optional
as always in this situation. But don't get my concern wrong here. I think that this is a great feature request but only if the default is determined at runtime and not part of the ABI.
It would also be great with APIs you cannot change (third party) and we wouldn't need ugly code like
class C(a: String?) { val a = a :? "default" }
that causes resolution issues throughout the rest of any init code.
The current proposal in the task doesn't solve the null issue either... But an overload based solution would tick the runtime requirement.
e
I don't see why Ilya's proposal would have any problem with null
f
Erm, it's using
:?
so it only works with null not meaning "use the default". Same as with the null based default that works today explained above. The proposal from the ticket is also using
:?
so it's the same again. Only stating this because you explicitly asked "what if null is a valid value" 'cause none of the proposals can adress that.
Oh, well, the if you mean. Yeah, that works.
d
That's just an example, the usage of
?:
It could be and
if
/
else
with any condition. The proposal I wrote here and, incidentally, at that link, suggest using
_
to indicate "use the default value".
f
Jap, all good, I just forgot (while writing the last responses) about the proposal in the ticket. I had a look at what is exactly generated by Kotlin and for a
fun f(a: String, b: String = "default", c: String) {}
(which is a seriously weird signature) we get something similar to the following:
Copy code
public final class MainKt {
   public static final void f(@NotNull String a, @NotNull String b, @NotNull String c) {
      Intrinsics.checkParameterIsNotNull(a, "a");
      Intrinsics.checkParameterIsNotNull(b, "b");
      Intrinsics.checkParameterIsNotNull(c, "c");
   }

   // $FF: synthetic method
   public static void f$default(String var0, String var1, String var2, int var3, Object var4) {
      if ((var3 & 2) != 0) {
         var1 = "default";
      }

      f(var0, var1, var2);
   }
}
So it should actually always be resolved at runtime if we do
f("x", _, "y")
we would have
f$default("x", null, 2, "y")
and thus
"default"
for
b
. But if our library is changed to have
"foo"
as the new default we would get
"foo"
and no issues with ABI. 😎
m
@Fleshgrinder not exactly. The internal call would be
f$default("x", null, "y" 2, null)
. The last parameter of type
Any
is reserved for future use.
The problem with a placeholder like
_
is that it’s pretty useless when used directly.
f("x", _, "y")
is the same as
f(a="x", c="y")
. The typical scenarios involve conditionals, e.g.
Copy code
f(
   a = "x"
   b = if (foo) bar else _,
   c = "y"
)
The question is then what the type of
if (foo) bar else _
is. That’s non-trivial I think 😕 Another option may be an intrinsic or special syntax that can only be used in exactly that location with exactly one boolean condition. That way we can avoid adding another type. E.g.
Copy code
f(
   a = "x",
   b = argumentIf(foo) { bar },
   c = "y"
)
Or even simpler:
Copy code
f(
   a = "x",
   b = if (foo) bar,
   c = "y"
)
e
type of
if (foo) bar else _
could be
T?!
(just kidding, I don't want that to really be introduced)
f
Isn't the type of the default fixed to the type of the parameter?
e
if we wanted to allow
Copy code
val maybe = if (foo) bar else _
f(maybe)
then it would have to be a separate type
I don't think that would be a great idea though
m
It shouldn’t be possible to assign it to a value. That would require a new type indeed. There are many ways already to define optional values.
?
,
Optional
,
Either
, a
hasValue: Boolean
, etc. Introducing a new type just for this rare case of calling functions explicitly with default arguments isn’t really worth that, is it?
f
Nope, imho it should be used only for what we're discussing here and nothing else. Kinda like Rust's rest operator in structs.