How to use two consecutive strings in `scala.util.CommandLineParser`?

I’m puzzled with the idiomatic way of using two consecutive strings from user input CLI to construct a variable of an arbitrary type. Since the fromString method of given CommandLineParser.FromString[T] does accept only single Strings, how is it possible to use two Strings for just one conversion? I guess the internals use spaces as the split character for creating an Array[String] from user’s stdin, so I want to override this assumption to create my own Array[String]. For example, an input of:

:~$ scala run MyProgramm.scala -- -i input-file.txt -o output-file.html

now creates an Array("-i", "input-file.txt", "-o", "output-file.html") and if I use the CommandLineParser I get four separate conversions from the args, instead I want two conversions each consuming pairs of input. Is it possible to get an Array("-i input-file.txt", "-o output-file.html") from command line to be furthur processed by the CommandLineParser?

This is the standard for how to construct command-lines in a Unix-like terminal. If you want to pass spaces as part of a single argument, either use single quotes, e.g. scala ... -- '-i input-file.txt' '-o output-file.txt', or use a more full-featured argument parser. You can, of course, iterate over the array yourself and figure out which are options and which are arguments to options. The built-in parsing is very simple, just barely more than doing it from scratch, so you’re expected to handle all the details that aren’t simply this-type-is-at-that-position.

Try mainargs, for instance.

2 Likes

Thanks, but unfortunately both approaches you suggest, are non-idiomatic to Scala. The first approach needs user intervention, and the problem with the second one is that the compiler does some automatic desugaring by CommandLineParser given instance that will be lost and should be done by hand if I iterate over the array.

For the record, note that scala.util.CommandLineParser is barely useful, it is there just for simple scripts.
A lot of folks actually argued that if it was going to be that minimal it should not be included in favour of more robust solutions like mainargs, scopt, decline, etc.

So if you got to the point where the default one doesn’t fit your needs, I would not discard using a different library as “non idiomatic”. If anything, using libraries is the idiomatic thing in Scala.

5 Likes

Just to reinforce the point about using libraries being idiomatic: scala-cli (which is what scala calls under the hood if you use a recent version) supports one-line dependency requests in at the top of the file (e.g. \\> using dep com.lihaoyi::mainargs:0.7.6).

You are expected to want to use other libraries.

3 Likes

Unscoring this: in many ways, this is kind of the point of how Scala ticks. Where most languages hard-code all sorts of things in the language and standard library, Scala very intentionally deconstructs those decisions into the most elemental concepts, so that as much as possible can be implemented in “userland”.

The result is that Scala’s standard library exists in conversation with the broader ecosystem. It is very, very deliberately not the be-all and end-all of working in Scala – instead, it’s sort of the lowest common denominator, good for simple stuff but not optimal for much beyond that.

In general, “power features” come from the ecosystem instead of stdlib. Sometimes, when something becomes seriously popular and “everyone” is using it, it will get lifted up into stdlib. Occasionally, we’ll decide that a feature isn’t pulling its weight in the language or stdlib, and it will be extracted out to a separate library.

But basically, all serious Scala work uses libraries; indeed, just about anything of any real scale uses lots of libraries. So IMO, just about the most un-idiomatic thing one can do is try to work without third-party libraries.

4 Likes