pattern match args and give error messages in a lightweight Scala script
I write a number of simple scala scripts that end up starting with a simple pattern match on args
like:
val Array(path, foo, whatever) = args
// .. rest of the script uses "path", "foo", etc.
Of course, if I supply the wrong number of arguments, I get an inscrutable error like:
scala.MatchError: [Ljava.lang.String;@7786df0f
开发者_C百科 at Main$$anon$1.<init>(FollowUsers.scala:5)
...
Is there an easy way to give a more useful error message? My current workaround is to do something like:
args match {
case Array(path, foo, whatever) => someFunction(path, foo, whatever)
case _ => System.err.println("usage: path foo whatever")
}
def someFunction(path: String, foo: String, whatever: String) = {
// .. rest of the script uses "path", "foo", etc.
}
But that feels like a lot of boilerplate what with having to define a whole other function, and having to repeat "path", "foo" and "whatever" in so many places. Is there a better way? I guess I could lose the function and put the body in the match statement, but that seems less readable to me.
I know I could use one of the many command line argument parsing packages, but I'm really looking for something extremely lightweight that I don't have to add a dependency and modify my classpath for.
How about?
val Array(path, foo, whatever) = if (args.length == 3) args
else throw new Exception("usage:path foo whatever")
==edit==
based on Randall's comment:
require(args.length == 3, "usage: path foo whatever")
val Array(path, foo, whatever) = args
That's minimum boilerplate. Your vals are in scope, you don't have to deal with closing brace and you get the usage error message.
scala> val args = Array("evil", "mad", "scientist")
args: Array[java.lang.String] = Array(evil, mad, scientist)
scala> def logToConsole(th: Throwable) { Console.err.println("Usage: path foo bar") }
logToConsole: (th: Throwable)Unit
scala> handling(classOf[MatchError]) by logToConsole apply {
| val Array(path, foo, bar) = args
| println(path)
| }
evil
scala> handling(classOf[MatchError]) by logToConsole apply {
| val Array(path, foo, bar) = Array("#fail")
| println(path)
| }
Usage: path foo bar
One way is to catch MatchError:
try {
val Array(path, foo, whatever) = args
} catch {
case _: MatchError => System.err.println("usage: path foo whatever")
}
Struck me that maybe the new util.control.Exception might have a solution:
import scala.util.control.Exception
Exception.handling(classOf[scala.MatchError]).by{
e => System.err.println("usage: path foo whatever")
} {
val Array(path, foo, whatever) = args
// .. rest of the script uses "path", "foo", etc.
}
This at least puts the error handling first and keeps the rest of the code together, though it makes me a little nervous to have such a large try block (that second block with the Array pattern matching is essentially all in the same try block being handled by Exception.handling).
EDIT: Looks like Missing Faktor posted about the same thing too, but with an explicitly defined function and an explicit call to apply.
精彩评论