开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜