How to pattern match on Scala Scopt optional arguments - scala

I have a simple Scops parser that looks like
val parser: scopt.OptionParser[Config] = new scopt.OptionParser[Config]("my-app") {
head("scopt", "3.x")
(...)
opt[String]('q', "query")
.text("The query.")
.action { (value, conf) => conf.copy(
query = value
)}
.optional
}
(...)
parser.parse(args, Config()) match {
case Some(config) => drive(config)
(...)
In my driver function I want to initialize a parameter with what the user provided via the argument or some default value otherwise.
I could do something like
var myQuery = _
if config.query != "" myQuery = config.query
else myQuery = config.query
But, (i) I doubt that testing against the empty string is the right way to check if the user provided an optional argument or not and (ii) that doesn't look like a very functional to write this in Scala.
Question: Is there a nice functional way to do this? I considered pattern matching like below but the Scopt arguments do not seem to be returned as Option[String]
val myQuery: String = config.query match {
case Some(q) => q
case None => "This is the default query"
}

Just make Config#query an Option[String] (with None as default) and change the query = value line to query = Some(value).

Related

How to case match on a function with optional parameters

I want to be able to have a method with an optional parameter. When that optional parameter is present, it will call a specific DB query that returns records filtered with the optional parameter. If that optional parameter isn't present, I want it to call a different DB query that doesn't filter by the optional parameter.
The first query is not written yet, but would have the same return structure and types. The second query is written, and works fine without the optional parameter and the cases.
def getRecords(id: String, type: String = ""): Future[List[Set[String]]] = {
case Some(type) =>
val query =>
s"""
| ...
""".stripMargin
case _ =>
val query =>
s"""
| ...
""".stripMargin
record = get(dbResult).asList.asScala.map(_.toString).toSet
}
The error I recieve is
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: scala.concurrent.Future[List[Set[String]]]
: Future[List[Set[String]]] = {
^
Can someone please explain to me what the error means? and also how to design my method to work the way I would like it to?
Note: Some of the details of the method have been omitted. It essentially just returns a record of the return type and whatever data was taken by one of the queries.
Re your comment, sure, here you go:
def getRecords(id: String, `type`: Option[String] = None): Future[List[Set[String]]] = {
val matchResult = `type` match {
case Some(t) => //t is your string
val query =
s"""
| ...
""".stripMargin
//do something with your query val
case _ =>
val query =
s"""
| ...
""".stripMargin
//do something with your query val
}
//use your matchResult here, whatever that ends up being
//not sure how this works but just copied from your question:
get(dbResult).asList.asScala.map(_.toString).toSet
}
Obviously you'll have to use query somewhere, but I assume you've just simplified that away. If you're worried about empty strings, you can add a guard to your first case clause: case Some(t) if t.nonEmpty => .... type is backticked because it's a keyword. If you use a non-keyword name you won't need the backticks.

Scala - How to safely operate on a map element

I want to get an element from a mutable map and do an operation on it.
For example I want to change his name value (the element on the map will be with the new value)
and I want to return it in the end
to start I wrote a working code but it is very Java
var newAppKey: AppKey = null
val appKey = myMap(request.appKeyId)
if (appKey != null) {
newAppKey = appKey.copy(name = request.appKeyName)
myMap.put(appKey.name, newAppKey)
newAppKey
} else {
newAppKey = null
}
This code works but it very java.
I though about something like
val newAppKey = appIdToApp(request.appKeyId) match {
case: Some(appKey) => appKey.copy(name = request.appKeyName)
case: None => None{AppKey}
}
Which doesn't compile or updates the myMap object with the new value.
How do I improve it to scala concepts.
Simply:
val key = request.appKeyId
val newValueOpt = myMap.get(key).map(_.copy(name = request.appKeyName))
newValueOpt.foreach(myMap.update(key, _))
There are a couple of mistakes in your code.
case: Some(appKey) => appKey.copy(name = request.appKeyName)
This syntax for case is incorrect. It should be
case Some(appKey) => appKey.copy(name = request.appKeyName)
Also, the return type of your expression is currently Any (Scala equivalent of Object), because your success case returns an object of type (appKey's type) whereas the failure case returns a None, which is of type Option. To make things consistent, your success case should return
Some(appKey.copy(name = request.appKeyName))
While there are better ways to deal with Options than pattern matching, the corrected code would be
val newAppKey = appIdToApp(request.appKeyId) map (appKey =>
appKey.copy(name = request.appKeyName))

Match/case for checking the expression results not matching pattern in Scala

This is the Scala code to check what container contains the label.
var typeName:JString = ""
if (stringTypes.contains(label)) {
typeName = "string"
} else if (floatingTypes.contains(label)) {
typeName = "float"
} else if (encodingTypes.contains(label)) {
typeName = "encoding"
} else if (rangeTypes.contains(label)) {
typeName = "range"
}
Can I make it better with match/case or similar? I see that the match/case matches the pattern not checking if the expression returns true or not. So, the following code does not work.
val typeName = label match {
case rangeTypes.contains(label) => "range"
...
you can use guards:
val typeName = label match {
case label if rangeTypes.contains(label) => "range"
...
or create a custom unapply method:
object TypeName {
def unapply(label: String): Option[String] =
if (rangeTypes.contains(label)) Some("range")
else if(floatingTypes.contains(label)) Some("float")
... else Some("") // this means that you won't have a MatchError but an empty string if nothing matches
}
val TypeName(typeName) = label
You have a lot of repetition going on there. I actually try to use pattern matching or if-else chains as a last resort. Functional programming works best when you're working on containers rather than syntactic structures, so I would do it by putting the type-to-string mapping into a Map, like:
val types = Map(stringTypes -> "string",
floatingTypes -> "float",
encodingTypes -> "encoding",
rangeTypes -> "range")
types find (_._1 contains label) map (_._2) getOrElse "label not found"

Scala: parsing an API parameter

My API currently take an optional parameter named gamedate. It is passed in as a string at which time I later parse it to a Date object using some utility code. The code looks like this:
val gdate:Option[String] = params.get("gamedate")
val res = gdate match {
case Some(s) => {
val date:Option[DateTime] = gdate map { MyDateTime.parseDate _ }
val dateOrDefault:DateTime = date.getOrElse((new DateTime).withTime(0, 0, 0, 0))
NBAScoreboard.findByDate(dateOrDefault)
}
case None => NBAScoreboard.getToday
}
This works just fine. Now what I'm trying to solve is I'm allowing multiple gamedates get passed in via a comma delimited list. Originally you can pass a parameter like this:
gamedate=20131211
now I want to allow that OR:
gamedate=20131211,20131212
That requires modifying the code above to try to split the comma delimited string and parse each value into a Date and change the interface to findByDate to accept a Seq[DateTime] vs just DateTime. I tried running something like this, but apparently it's not the way to go about it:
val res = gdates match {
case Some(s) => {
val dates:Option[Seq[DateTime]] = gdates map { _.split(",").distinct.map(MyDateTime.parseDate _ )}
val datesOrDefault:Seq[DateTime] = dates map { _.getOrElse((new DateTime).withTime(0, 0, 0, 0))}
NBAScoreboard.findByDates(datesOrDefault)
}
case None => NBAScoreboard.getToday
}
What's the best way to convert my first set of code to handle this use case? I'm probably fairly close in the second code example I provided, but I'm just not hitting it right.
You mixed up the containers. The map you call on dates unpackes the Option so the getOrElse is applied to a list.
val res = gdates match {
case Some(s) =>
val dates = gdates.map(_.split(",").distinct.map(MyDateTime.parseDate _ ))
val datesOrDefault = dates.getOrElse(Array((new DateTime).withTime(0, 0, 0, 0)))
NBAScoreboard.findByDates(datesOrDefault)
case _ =>
NBAScoreboard.getToday
}
This should work.

How to use scalax.io.CommandLineParser?

I want to create a class that takes string array as a constructor argument and has command line option values as members vals. Something like below, but I don't understand how the Bistate works.
import scalax.data._
import scalax.io.CommandLineParser
class TestCLI(arguments: Array[String]) extends CommandLineParser {
private val opt1Option = new Flag("p", "print") with AllowAll
private val opt2Option = new Flag("o", "out") with AllowAll
private val strOption = new StringOption("v", "value") with AllowAll
private val result = parse(arguments)
// true or false
val opt1 = result(opt1Option)
val opt2 = result(opt2Option)
val str = result(strOption)
}
Here are shorter alternatives to that pattern matching to get a boolean:
val opt1 = result(opt1Option).isInstanceOf[Positive[_]]
val opt2 = result(opt2Option).posValue.isDefined
The second one is probably better. The field posValue is an Option (there's negValue as well). The method isDefined from Option tells you whether it is a Some(x) or None.
I'm not personally familiar with Scalax or Bistate in particular, but just looking at the scaladocs, it looks like a left-right disjunction. Scala's main library has a monad very much like this (Either), so I'm surprised that they didn't just use the standard one.
In essence, Bistate and Either are a bit like Option, except their "None-equivalent" can contain a value. For example, if I were writing code using Either, I might do something like this:
def div(a: Int, b: Int) = if (b != 0) Left(a / b) else Right("Divide by zero")
div(4, 2) match {
case Left(x) => println("Result: " + x)
case Right(e) => Println("Error: " + e)
}
This would print "Result: 2". In this case, we're using Either to simulate an exception. We return an instance of Left which contains the value we want, unless that value cannot be computed for some reason, in which case we return an error message wrapped up inside an instance of Right.
So if I want to assign to variable boolean value of whether flag is found I have to do like below?
val opt1 = result(opt1Option) match {
case Positive(_) => true
case Negative(_) => false
}
Isn't there a way to write this common case with less code than that?