I am reading Lihaoyi's presentation of his parser combinators framework while knowing only the basics of Scala.
I came across this line that I don't understand at all:
val Parsed.Success(2, _) = parse("1+1", expr(_))
Coming from Java, it looks weird. Anybody knows what it does?
Thanks in advance.
https://www.lihaoyi.com/fastparse/
What would be the equivalent in Java?
Scala knows Extractor Objects - see docs.scala-lang.org
They are mostly used for pattern matching - see docs.scala-lang.org
So this can be used with vals:
val customer2ID = CustomerID("Nico")
val CustomerID(name) = customer2ID
println(name) // prints Nico
Your example will throw a scala.MatchError if the parser would not work.
Try val Parsed.Success(2, _) = parse("1+2", expr(_)) // should be 3
Other answers explained what this is technically, but why did the author of fastparse write
val Parsed.Success(2, _) = parse("1+1", expr(_))
where we see no variable is actually bound in the sense of being usable after this statement executes? This kind of looks like a side-effect returning unit. I believe the author is deploying this technique as an alternative to assertion
assert(parse("1+1", expr(_)) == Parsed.Success(2, 3))
but they want to emphasise they do not care about the index value of Success[+T](value: T, index: Int) which they cannot do using regular assert
assert(parse("1+1", expr(_)) == Parsed.Success(2, _)) // error: missing parameter type for expanded function
Function parse is returning a Parsed[T], which is matched against the extractor of the Success case class (inside the object Parsed).
It could be rewritten as:
parse("1+1", expr(_)) match {
case Parsed.Success(2, _/*index*/) => ???
}
The previous make it obvious that it's not an exhaustive match (and so the val pattern would raise a MatchError).
Could also be used as bellow, to be more explicit and avoid MatchError.
val res: Option[(Int/* value */, Int/* index */)] =
Parsed.Success.unapply(parse("1+2", expr(_)))
Related
I have a for comprehension like this:
for {
(value1: String, value2: String, value3: String) <- getConfigs(args)
// more stuff using those values
}
getConfigs returns an Either[Throwable, (Seq[String], String, String)] and when I try to compile I get this error:
value withFilter is not a member of Either[Throwable,(Seq[String], String, String)]
How can I use this method (that returns an Either) in the for comprehension?
Like this:
for {
tuple <- getConfigs()
} println(tuple)
Joking aside, I think that is an interesting question but it is misnamed a bit.
The problem (see above) is not that for comprehensions are not possible but that pattern matching inside the for comprehension is not possible within Either.
There is documentation how for comprehensions are translated but they don't cover each case. This one is not covered there, as far as I can see. So I looked it up in my instance of "Programming in Scala" -- Second Edition (because that is the one I have by my side on dead trees).
Section 23.4 - Translation of for-expressions
There is a subchapter "Translating patterns in generators", which is what is the problem here, as described above. It lists two cases:
Case One: Tuples
Is exactly our case:
for ((x1, …, xn) <- expr1) yield expr2
should translate to expr1.map { case (x1, …, xn) => expr2).
Which is exactly what IntelliJ does, when you select the code and do an "Desugar for comprehension" action. Yay!
… but that makes it even weirder in my eyes, because the desugared code actually runs without problems.
So this case is the one which is (imho) matching the case, but is not what is happening. At least not what we observed. Hm?!
Case two: Arbitrary patterns
for (pat <- expr1) yield expr2
translates to
expr1 withFilter {
case pat => true
case _ => false
} map {
case pat => expr2
}
where there is now an withFilter method!
This case totally explains the error message and why pattern matching in an Either is not possible.
The chapter ultimately refers to the scala language specification (to an older one though) which is where I stop now.
So I a sorry I can't totally answer that question, but hopefully I could hint enough what is the root of the problem here.
Intuition
So why is Either problematic and doesn't propose an withFilter method, where Try and Option do?
Because filter removes elements from the "container" and probably "all", so we need something that is representing an "empty container".
That is easy for Option, where this is obviously None. Also easy for e.g. List. Not so easy for Try, because there are multiple Failure, each one can hold a specific exception. However there are multiple failures taking this place:
NoSuchElementException and
UnsupportedOperationException
and which is why Try[X] runs, but an Either[Throwable, X] does not.
It's almost the same thing, but not entirely. Try knows that Left are Throwable and the library authors can take advantage out of it.
However on an Either (which is now right biased) the "empty" case is the Left case; which is generic. So the user determines which type it is, so the library authors couldn't pick generic instances for each possible left.
I think this is why Either doesn't provide an withFilter out-of-the-box and why your expression fails.
Btw. the
expr1.map { case (x1, …, xn) => expr2) }
case works, because it throws an MatchError on the calling stack and panics out of the problem which… in itself might be a greater problem.
Oh and for the ones that are brave enough: I didn't use the "Monad" word up until now, because Scala doesn't have a datastructure for it, but for-comprehensions work just without it. But maybe a reference won't hurt: Additive Monads have this "zero" value, which is exactly what Either misses here and what I tried to give some meaning in the "intuition" part.
I guess you want your loop to run only if the value is a Right. If it is a Left, it should not run. This can be achieved really easy:
for {
(value1, value2, value3) <- getConfigs(args).right.toOption
// more stuff using those values
}
Sidenote: I don't know whats your exact use case, but scala.util.Try is better suited for cases where you either have a result or a failure (an exception).
Just write Try { /*some code that may throw an exception*/ } and you'll either have Success(/*the result*/) or a Failure(/*the caught exception*/).
If your getConfigs method returns a Try instead of Either, then your above could would work without any changes.
You can do this using Oleg's better-monadic-for compiler plugin:
build.sbt:
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.2.4")
And then:
object Test {
def getConfigs: Either[Throwable, (String, String, String)] = Right(("a", "b", "c"))
def main(args: Array[String]): Unit = {
val res = for {
(fst, snd, third) <- getConfigs
} yield fst
res.foreach(println)
}
}
Yields:
a
This works because the plugin removes the unnecessary withFilter and unchecked while desugaring and uses a .map call. Thus, we get:
val res: Either[Throwable, String] =
getConfigs
.map[String](((x$1: (String, String, String)) => x$1 match {
case (_1: String, _2: String, _3: String)
(String, String, String)((fst # _), (snd # _), (third # _)) => fst
}));
I think the part you may find surprising is that the Scala compiler emits this error because you deconstruct the tuple in place. This is surprisingly forces the compiler to check for withFilter method because it looks to the compilers like an implicit check for the type of the value inside the container and checks on values are implemented using withFilter. If you write your code as
for {
tmp <- getConfigs(args)
(value1: Seq[String], value2: String, value3: String) = tmp
// more stuff using those values
}
it should compile without errors.
json4 has the following types:
sealed abstract class JValue
case class JString(s: String) extends JValue
//etc
I have the following json value:
val json: JValue = JString("hi")
and I use it in a for-comprehension as such:
val token = for {
JString(s) <- json
} yield s
Here is the question:
As it is, the token will be evaluated as List("hi") namely an instance of the type List[String]. My understanding was that it should instead be Option[String]. why Option -> List?
IntelliJ's type-"inference" helper, suggests setting the type JValue for the result of the for-comprehension. When you that however, you get a compile error. What's exactly at fault here? and why is the confusion happening?
Why List[String]?
Well, as you can see here, that's what the json4s authors chose. They could have gone with Vector[String], or Array[String], but decided upon List[String]. I assume they had good reasons.
What's withFilter() got to do with it?
As we all learned on the first day of Scala class, for is not a control-flow language construct, but is actually syntactic sugar used to prettify nested map/flatMap constructs.
for {
b <- a // flatMap()
if b.isSomeCondition // withFilter()
c <- b // map()
} yield c
It turns out that withFilter() is also employed when pattern matching in a for construct. So this...
for {
JString(s) <- json
} yield s
...gets translated into this (roughly).
json.withFilter{
case JString((s # _)) => true
case _ => false
}.map{
case JString(s # _) => s
}
The authors of json4s decided that withFilter() should return a List and thus that's the result of your for comprehension.
IntelliJ's confusion.
IntelliJ's syntax checker and suggestion engine is pretty good, but it's not perfect. Scala code can get rather complicated and it's not uncommon for IntelliJ to get confused. Trust the compiler. If it compiles without warning then just ignore the faulty type suggestions.
I have a variable underlying of type Option[mutable.Traversable[Field]]
All I wanted todo in my class was provide a method to return this as Sequence in the following way:
def toSeq: scala.collection.mutable.Seq[Field] = {
for {
f <- underlying.get
} yield f
}
This fails as it complains that mutable.traversable does not conform to mutable.seq. All it is doing is yielding something of type Field - in my mind this should work?
A possible solution to this is:
def toSeq: Seq[Field] = {
underlying match {
case Some(x) => x.toSeq
case None =>
}
}
Although I have no idea what is actually happening when x.toSeq is called and I imagine there is more memory being used here that actually required to accomplish this.
An explanation or suggestion would be much appreciated.
I am confused why you say that "I imagine there is more memory being used here than actually required to accomplish". Scala will not copy your Field values when doing x.toSeq, it is simply going to create an new Seq which will have pointers to the same Field values that underlying is pointing to. Since this new structure is exactly what you want there is no avoiding the additional memory associated with the extra pointers (but the amount of additional memory should be small). For a more in-depth discussion see the wiki on persistent data structures.
Regarding your possible solution, it could be slightly modified to get the result you're expecting:
def toSeq : Seq[Field] =
underlying
.map(_.toSeq)
.getOrElse(Seq.empty[Field])
This solution will return an empty Seq if underlying is a None which is safer than your original attempt which uses get. I say it's "safer" because get throws a NoSuchElementException if the Option is a None whereas my toSeq can never fail to return a valid value.
Functional Approach
As a side note: when I first started programming in scala I would write many functions of the form:
def formatSeq(seq : Seq[String]) : Seq[String] =
seq map (_.toUpperCase)
This is less functional because you are expecting a particular collection type, e.g. formatSeq won't work on a Future.
I have found that a better approach is to write:
def formatStr(str : String) = str.toUpperCase
Or my preferred coding style:
val formatStr = (_ : String).toUpperCase
Then the user of your function can apply formatStr in any fashion they want and you don't have to worry about all of the collection casting:
val fut : Future[String] = ???
val formatFut = fut map formatStr
val opt : Option[String] = ???
val formatOpt = opt map formatStr
While going through Spray.io examples library I came across this declaration of val in FileUploadHandler example of routing app.
val Some(HttpHeaders.Content-Type(ContentType(multipart: MultipartMediaType, _))) = header[HttpHeaders.Content-Type]
As per my understanding the variable declaration goes as val <identifier> = ...
Please help in understanding this paradigm of syntax.
val is a bit more complex than just an assignment operator.
A definition
val p = e
where p is not just a variable name, is expanded to
val x = e match { case p => x }
Take a loot at the simplest example:
val Some(s) = Some(5)
As a result, s would be equal 5.
In your example header[HttpHeaders.Content-Type] is matched against Some(...).
According to Scala language spec: Value definitions can alternatively have a pattern as left-hand side. Watch out for PatDef in the document.
Section "Patterns in Value Definitions" of Daniel Westheide's Blog gives a nice overview on the usage.
You're looking for extractors/mattern matching in scala, please see http://www.scala-lang.org/old/node/112.
You need a simple form of it, take a look at this snippet:
scala> val Some(t) = Some("Hello")
t: String = Hello
Im using scala Map#get function, and for every accurate query it returns as Some[String]
IS there an easy way to remove the Some?
Example:
def searchDefs{
print("What Word would you like defined? ")
val selection = readLine
println(selection + ":\n\t" + definitionMap.get(selection))
}
When I use this method and use the following Input:
What Word would you like defined? Ontology
The returned Value is:
Ontology:
Some(A set of representational primitives with which to model a domain of knowledge or discourse.)
I would like to remove the Some() around that.
Any tips?
There are a lot of ways to deal with the Option type. First of all, however, do realize how much better it is to have this instead of a potential null reference! Don't try to get rid of it simply because you are used to how Java works.
As someone else recently stated: stick with it for a few weeks and you will moan each time you have to get back to a language which doesn't offer Option types.
Now as for your question, the simplest and riskiest way is this:
mymap.get(something).get
Calling .get on a Some object retrieves the object inside. It does, however, give you a runtime exception if you had a None instead (for example, if the key was not in your map).
A much cleaner way is to use Option.foreach or Option.map like this:
scala> val map = Map(1 -> 2)
map: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> map.get(1).foreach( i => println("Got: " + i))
Got: 2
scala> map.get(2).foreach( i => println("Got: " + i))
scala>
As you can see, this allows you to execute a statement if and only if you have an actual value. If the Option is None instead, nothing will happen.
Finally, it is also popular to use pattern matching on Option types like this:
scala> map.get(1) match {
| case Some(i) => println("Got something")
| case None => println("Got nothing")
| }
Got something
I personally like using .getOrElse(String) and use something like "None" as a default i.e. .getOrElse("None").
I faced similar issue, replaced with .Key() to resolve.
Solution:
definitionMap(selection)
In modern scala you can just map(key)