https://kotlinlang.org logo
#getting-started
Title
# getting-started
p

Piotr Krzemiński

02/21/2023, 1:17 PM
is there a Kotlin counterpart of keyword-only args in Python? I want to be able to force the call site of a function to use the named parameters. I found https://stackoverflow.com/questions/37394266/how-can-i-force-calls-to-some-constructors-functions-to-use-named-arguments but it looks shady - produces some warnings, I’m wondering if we could get something closer to the intent
g

gildor

02/21/2023, 1:23 PM
No, there is no such feature, only feature request on official issue tracker
p

Piotr Krzemiński

02/21/2023, 1:24 PM
👌 1
g

gildor

02/21/2023, 1:25 PM
Be free to add your use case, always helpful
👌 1
c

CLOVIS

02/21/2023, 1:25 PM
Why do you want this?
p

Piotr Krzemiński

02/21/2023, 1:26 PM
in the context of https://github.com/krzema12/github-workflows-kt/, I generate a Kotlin class for each GitHub action. The users occasionally have issues when using positional arguments because the GitHub Actions API doesn’t rely on positions but on dictionary of arguments instead. I’d like to reflect it on the Kotlin layer as well
also see the warning in one of the recent releases: https://github.com/krzema12/github-workflows-kt/releases/tag/v0.37.0 “Warning: the below changes may break your workflow code. …”
g

gildor

02/21/2023, 1:27 PM
I feel that this feature is somewhat related (not sure that it’s blocked per se, just related) to named destructuring which is also proposed, maybe I’m wrong though
p

phldavies

02/21/2023, 1:34 PM
As prior art, there is the
CopyWithoutNamedArguments
compilation warning on
.copy(...)
usage without named arguments - it would be nice to have a
@RequireNamedArguments
annotation you can add where required to provide a similar warning (which could be done using #detekt rule for now)
👍 2
a

Adam S

02/21/2023, 2:17 PM
the only strict way I can think of is to make every argument a value class, which is verbose, annoying to use, and complicated to set up. As a soft measure, you could create a custom IntelliJ inspection. But to share this with everyone requires an IntelliJ plugin...
p

Piotr Krzemiński

02/21/2023, 2:18 PM
the only strict way I can think of is to make every argument a value class, which is verbose, annoying to use, and complicated to set up.
I’d prefer the good old Java builder over this 🙂 but I probably won’t implement it
But to share this with everyone requires an IntelliJ plugin...
yeah, exactly - the same with linter rules
a

Adam S

02/21/2023, 2:20 PM
for this specific functionality an IJ plugin would be too much work, but if you want other functionality as well, like having a custom script context instead of using
.main.kts
, then an IJ plugin is more tempting...
🤔 1
p

Piotr Krzemiński

02/21/2023, 2:26 PM
following up on this particular idea ☝️ here, but this very thread still needs to be decoupled from the scripting - a generic solution would be nice
a

Adam S

02/21/2023, 2:43 PM
maybe using a lambda builder might be more explicit? At least it requires property names. But it's possible to forget to add parameters.
Copy code
import kotlin.properties.Delegates.notNull

class Parameters {
  var foo: String by notNull()
  var bar: Int by notNull()
  
  override fun toString() = "Parameters(foo=$foo,bar=$bar)"
}

fun someFunction(init: Parameters.() -> Unit) {
  val params = Parameters()
  params.init()
  println(params)
}

fun main() {
  someFunction {
    foo = "abc"
    bar = 123
  }
}
Playground link
p

Piotr Krzemiński

02/21/2023, 2:44 PM
But it’s possible to forget to add parameters.
exactly - we’d sacrifice one kind of safety measure for the other, I’d rather stay with the current approach, If we had a mechanism to enforce using all intended parameters in a lambda, then it’s a sensible thing to consider
p

phldavies

02/21/2023, 3:02 PM
It's not pretty, but achieves what you want:
Copy code
sealed interface Marker

fun forcedNamedArguments(vararg _ignore: Marker,
                        name: String,
                        age: Int,
                        nickname: String = name) {
	println("$name ($nickname) is $age years old")
}

fun main() {
    forcedNamedArguments(
        name = "Robert", 
        age = 32, 
        nickname = "Bob"
    )
}
p

Piotr Krzemiński

02/21/2023, 3:05 PM
yeah, pretty much what https://stackoverflow.com/a/37394267 suggests - but thanks anyway! I’m considering using it, especially in the generated code
p

phldavies

02/21/2023, 3:30 PM
I think I prefer using the
sealed interface
approach as there’s no need for the suppressions, and the only downside is someone could call it with
myFun(*emptyArrayOf<Marker>(), "Robert", 32)
👍 1
p

Piotr Krzemiński

02/22/2023, 7:32 AM
the only downside is someone could call it with (…)
good point, however it would have to be a conscious decision, not an accident
7 Views