I'm thinking of trying to submit a PR with dsl ext...
# clikt
b
I'm thinking of trying to submit a PR with dsl extensions for clikt. Main use-case is more concise setup for kts scripting without having to deal with classes. Can I get some votes on how useful that would be here?
👍 1
👎 2
@jw By DSL I basically mean you can build out your entire cli command hierarchy without declaring a single class - only functions and lambdas. Here's some mock up api of how that might look like
Copy code
Clikt(name="git") {
  Command(name="commit") {
    val amend by option().flag()
    
    execute {
      // Do stuff the command is supposed to
    }
  }
  Command(name="push") { ... }
}.main(args)
This would be harder to maintain, but would bring less boilerplate which would make it great for kts scripts, but probably not full-fledged cli projects.
j
I don't really see any boilerplate that's been eliminated.
All of the same things are there. You just don't type "class" and have to remember to call
execute
instead of the compiler forcing an override.
b
All class and function declarations. Plus no instantiation (objects are a big no no in kts) nor subcommand wiring needed.
j
I think you're conflating boilerplate for verbosity and maybe even for code golfing a bit.
b
I've been using clikt with kts a lot recently and found classes tricky to work with. But might be just me here, thus asking for second opinions :)
j
I don't really understand how Kotlin scripts really change anything with regard to classes. In the few I've done with clikt private classes for the command have worked fine. I also don't see the problem with instantiation. Clikt properties creates at least a many allocations through the use of non-inlined lambdas so the command allocation doesn't even register (especially since owns little-to-no-fields and barely costs more than an object header)
It seems like a DSL solely for DSL sake. And that's fine. Kotlin encourages that. But I don't think there's a huge win here.
b
Guess most of my issues are just nits. That's nothing new though as I never particularly liked excessively working with classes to structure my trees 😀
Just feels a bit too imperative.
In any case, thanks for talking this through with me. I think I'll just spin up unrelated lib that solves my own particular itch without trying to make it generally useful nor official part of clikt.
j
Well, one of the things that would make this more appealing is that if something like this became the only way to define commands. And then separation into individual functions or classes becomes something the user has to do themselves, similar to how Ktor's DSL works.
I do think a class lends itself well to clikt's behavior, though, as you get a constructor to inject dependencies from the outside, a function to inject the arguments from the outside, and then a single function on the inside which only runs after argument processing has been bound to the properties.
b
I agree that given a choice to only have one - class approach is a lot more flexible.
Well this turned out to be more trivial than I've expected. Got all I ever wanted in 120 lines .
And the usage is like this
Copy code
Clikt("my-cmd") {
  Command("build") {
    val test by option("--test").flag()
  
    execute {
      println("BUILD test=${test}")
    }
  }
}.main(listOf("build", "--test"))
Nice thing about it is that compiler errors if you try to create new option delegates within the
execute {}
block.
Oh, wait - it does that inside classic
CliktCommand::run
method too. Nice!