Why is that the following will not compile
for {
s <- List.empty[String]
regex <- List.empty[scala.util.matching.Regex]
regex(ss) = s
if ss == "foo"
} yield s
But removing the if
for {
s <- List.empty[String]
regex <- List.empty[scala.util.matching.Regex]
regex(ss) = s
} yield s
or rearranging the order of the two lists in the for comprehension
for {
regex <- List.empty[scala.util.matching.Regex]
s <- List.empty[String]
regex(ss) = s
if ss == "foo"
} yield s
compiles?
Scalafiddle: http://scalafiddle.net/console/2519ff98d434cb522589f54a9c5fcf55
You can see the translated for-comprehension using this command:
scalac -Xprint:all <file>.scala
In your first example the resulting code looks like this (I cleaned up the output a bit):
List.empty[String]
.flatMap(((s) =>
List.empty[scala.util.matching.Regex]
.map(regex => {
private[this] val x$2 =
s match {
case (x$1#regex((ss#_))) => scala.Tuple2(x$1, ss)
};
val x$1 = x$2._1;
val ss = x$2._2;
scala.Tuple2(regex, x$1)
}).withFilter((x$3 => x$3 match {
case scala.Tuple2((regex#_), regex((ss#_))) => ss.$eq$eq("foo")
})).map(((x$4) => x$4 match {
case scala.Tuple2((regex#_), regex((ss#_))) => s
})))
)
The problem seems to be that the withFilter clause uses the expression regex(ss) directly in the case statement, but the value regex it is not defined there. I'm not sure if this can be considered a flaw in the language specification, or in the compiler. The error message is certainly not very helpful.
You can read up on the details in chapter 6.19 of the Scala language specification.
This might help you.
import scala.util.matching.Regex
import scala.util.control.Exception._
for {
s <- List.empty[String]
regex <- List.empty[scala.util.matching.Regex]
ss <- extract(regex, s)
if ss == "foo"
} yield s
def extract(regex: Regex, s: String): Option[String] = allCatch.opt {
val regex(ss) = s
ss
}
Related
I am trying to write a function that reads from a repository (which returns F[Option[Entry]] where F is a future) and then converts it to F[Option[Element]]. The function convert() takes a Seq[Entry] and returns ValidatedNel[LoadError, Seq[Element]]. I am trying to do this with for comprehensions however my issue is that the function returns F[Opt[F[Seq[Element]]]] instead of F[Opt[Element]]. What am I doing wrong? Any help would be great!
Note: ValidatedNel is a cats datatype
override def findByPath(key: String)(
implicit ME: MonadError[F, Throwable]
): F[Option[Element]] = {
val result = for {
optionEntry <- repo.findByKey(key)
} yield for {
entry <- optionEntry
elements = convert(Seq(entry))
} yield elements.fold(
reason => ME.raiseError[Seq[Element]](ClientError(reason)),
ME.pure
)
result
}
Try to use monad transformer OptionT and unpack head of Element sequence (since you pack a single Entry into sequence)
override def findByPath(key: String)(
implicit ME: MonadError[F, Throwable]
): F[Option[Element]] = {
val result = for {
entry <- OptionT(repo.findByKey(key))
elements = convert(Seq(entry))
res <- OptionT.liftF(elements.fold(
reason => ME.raiseError[Element](ClientError(reason)),
elems => ME.pure(elems.head)
))
} yield res
result.value
}
I wanted to apply pattern matching immediately after yield in for-comprehension yielding Option[Int] as follows:
object Main extends App {
val i: Int = for (
int1 <- i1();
int2 <- i2()
) yield (int2) match {
case Some(i) => i
case None => 0
}
def i1(): Option[Int] = None
def i2(): Option[Int] = None
}
DEMO
Unfortuantely it does not compile. Is there a way to avoid "temporary" Option[Int] result declaration?
How about just adding brackets?
val i: Int = (for {
int1 <- i1()
int2 <- i2()
} yield (int2)) match {
case Some(i) => i
case None => 0
}
Edit: Added curly brackets
Let's say you've got a bunch of methods:
def foo() : Try[Seq[String]]
def bar(s:String) : Try[String]
and you want to make a for-comprhension:
for {
list <- foo
item <- list
result <- bar(item)
} yield result
of course this won't compile since Seq cannot be used with Try in this context.
Anyone has a nice solution how to write this clean without breaking it into separate two for's?
I've came across this syntax problem for the thirds time and thought that it's about time to ask about this.
IMHO: Try and Seq is more than what you need to define a monad transformer:
Code for library:
case class trySeq[R](run : Try[Seq[R]]) {
def map[B](f : R => B): trySeq[B] = trySeq(run map { _ map f })
def flatMap[B](f : R => trySeq[B]): trySeq[B] = trySeq {
run match {
case Success(s) => sequence(s map f map { _.run }).map { _.flatten }
case Failure(e) => Failure(e)
}
}
def sequence[R](seq : Seq[Try[R]]): Try[Seq[R]] = {
seq match {
case Success(h) :: tail =>
tail.foldLeft(Try(h :: Nil)) {
case (Success(acc), Success(elem)) => Success(elem :: acc)
case (e : Failure[R], _) => e
case (_, Failure(e)) => Failure(e)
}
case Failure(e) :: _ => Failure(e)
case Nil => Try { Nil }
}
}
}
object trySeq {
def withTry[R](run : Seq[R]): trySeq[R] = new trySeq(Try { run })
def withSeq[R](run : Try[R]): trySeq[R] = new trySeq(run map (_ :: Nil))
implicit def toTrySeqT[R](run : Try[Seq[R]]) = trySeq(run)
implicit def fromTrySeqT[R](trySeqT : trySeq[R]) = trySeqT.run
}
and after you can use for-comrehension (just import your library):
def foo : Try[Seq[String]] = Try { List("hello", "world") }
def bar(s : String) : Try[String] = Try { s + "! " }
val x = for {
item1 <- trySeq { foo }
item2 <- trySeq { foo }
result <- trySeq.withSeq { bar(item2) }
} yield item1 + result
println(x.run)
and it works for:
def foo() = Try { List("hello", throw new IllegalArgumentException()) }
// x = Failure(java.lang.IllegalArgumentException)
You can take advantage of the fact that Try can be converted to Option, and Option to Seq:
for {
list <- foo.toOption.toSeq // toSeq needed here, as otherwise Option.flatMap will be used, rather than Seq.flatMap
item <- list
result <- bar(item).toOption // toSeq not needed here (but allowed), as it is implicitly converted
} yield result
This will return a (possibly empty, if the Trys failed) Seq.
If you want to keep all the exception detail, you'll need a Try[Seq[Try[String]]]. This can't be done with a single for comprehension, so you're best sticking with plain map:
foo map {_ map bar}
If you want to mingle your Trys and Seqs in a different way, things get fiddlier, as there's no natural way to flatten a Try[Seq[Try[String]]]. #Yury's answer demonstrates the sort of thing you'd have to do.
Or, if you're only interested in the side effects of your code, you can just do:
for {
list <- foo
item <- list
result <- bar(item)
} result
This works because foreach has a less restrictive type signature.
A Try can be converted to an Option, which you can than use in a for-comprehension. E.g.
scala> def testIt() = {
| val dividend = Try(Console.readLine("Enter an Int that you'd like to divide:\n").toInt)
| dividend.toOption
| }
testIt: ()Option[Int]
scala> for (x <- testIt()) println (x * x)
Enter an Int that you'd like to divide:
scala> for (x <- testIt()) println (x * x)
Enter an Int that you'd like to divide:
1522756
First time I entered "w", then second time 1234.
I'm quite new with Scala and especially the awesome pattern match. However, I find this code not working. I created a "dictionary" that has the match words, then I use a for-comprehension so each line will be matched up with the words in the dictionary.
This is the Map used to create regex.
val dictionary = Map(
"""will""" -> 1,
"""going to""" -> 2,
"""future""" -> 3
)
This is the main for-loop:
for (
ln <- file.getLines();
(word, loc) <- dictionary
){
val regex = word.r
ln match {
case regex(ln) => {totalLine += 1
println("Match detected: " + word)
val originalElem = doc.BOWVector(dictionary.get(ln).get)
doc.BOWVector.updated(dictionary.get(ln).get, originalElem+1) //vector is updated
}
case _ => {totalLine += 1}
}
}
When I use ln.contains("will") and it works! However the regex won't work. WHY?
A little closer:
scala> for (ln <- List("I will go"); (word, loc) <- dictionary) {
| val r = word.r.unanchored
| ln match { case r() => println(s"Yes $word") ; case _ => println(s"No $word") }}
Yes will
No going to
No future
The default anchored regex is ^will$.
If you want to not worry about the capturing groups, use a "sequence wildcard."
scala> for (ln <- List("I will go"); (word, loc) <- dictionary) {
| val r = word.r.unanchored
| ln match { case r(_*) => println(s"Yes $word") ; case _ => println(s"No $word") }}
Yes will
No going to
No future
scala> val dictionary = Map("wi(l*)" -> 1)
dictionary: scala.collection.immutable.Map[String,Int] = Map(wi(l*) -> 1)
scala> for (ln <- List("I will go"); (word, loc) <- dictionary) {
| val r = word.r.unanchored
| ln match { case r(_*) => println(s"Yes $word") ; case _ => println(s"No $word") }}
Yes wi(l*)
scala> for (ln <- List("I will go"); (word, loc) <- dictionary) {
| val r = word.r.unanchored
| ln match { case r(ells) => println(s"Yes $ells") ; case _ => println(s"No $word") }}
Yes ll
The problem is that scala.util.matching.Regex.unapplySeq returns matching groups
If regex doesn't contain groups (your case), in pattern matching it should be used in form:
scala> val regex1 = "foo".r
regex1: scala.util.matching.Regex = foo
scala> "foo" match { case regex1() => "match" }
res0: String = match
And with matching groups it will be
scala> val regex2 = "(foo)".r
regex2: scala.util.matching.Regex = (foo)
scala> "foo" match { case regex2(group) => s"match $group" }
res1: String = match foo
Number of groups can be arbitrary, but number of arguments in case statement should match
scala> val regex3 = "(foo)(\\d)".r
regex3: scala.util.matching.Regex = (foo)(\d)
scala> "foo1" match { case regex3(word, digit) => s"match $word $digit" }
res2: String = match foo 1
Also, see examples for unapplySeq scaladoc
I was playing with Scala 2.11's new macro features. I wanted to see if I could do the following rewrite:
forRange(0 to 10) { i => println(i) }
// into
val iter = (0 to 10).iterator
while (iter.hasNext) {
val i = iter.next
println(i)
}
I think I got fairly close with this macro:
def _forRange[A](c: BlackboxContext)(range: c.Expr[Range])(func: c.Expr[Int => A]): c.Expr[Unit] = {
import c.universe._
val tree = func.tree match {
case q"($i: $t) => $body" => q"""
val iter = ${range}.iterator
while (iter.hasNext) {
val $i = iter.next
$body
}
"""
case _ => q""
}
c.Expr(tree)
}
This produces the following output when called as forRange(0 to 10) { i => println(i) } (at least, it's what the show function gives me on the resultant tree):
{
val iter = scala.this.Predef.intWrapper(0).to(10).iterator;
while$1(){
if (iter.hasNext)
{
{
val i = iter.next;
scala.this.Predef.println(i)
};
while$1()
}
else
()
}
}
That looks like it should work, but there's a conflict between my manually defined val i and the i referenced in the spliced-in function body. I get the following error:
ReplGlobal.abort: symbol value i does not exist in$line38.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.
error: symbol value i does not exist in
scala.reflect.internal.FatalError: symbol value i does not exist in $line38.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.
And then a rather large stack trace, resulting in an "Abandoned crashed session" notification.
I can't tell if this is a problem with my logic (you simply can't splice in a function body that references a closed-over variable), or if it's a bug with the new implementation. The error reporting certainly could be better. It may be exacerbated by the fact that I'm running this on the Repl.
Is it possible to pull apart a function, separating the body from the closed-over terms, and rewrite it in order to splice the logic directly into a resulting tree?
When in doubt, resetAllAttrs:
import scala.language.experimental.macros
import scala.reflect.macros.BlackboxContext
def _forRange[A](c: BlackboxContext)(range: c.Expr[Range])(
func: c.Expr[Int => A]
): c.Expr[Unit] = {
import c.universe._
val tree = func.tree match {
case q"($i: $t) => $body" => q"""
val iter = ${range}.iterator
while (iter.hasNext) {
val $i = iter.next
${c.resetAllAttrs(body)} // The only line I've changed.
}
"""
case _ => q""
}
c.Expr(tree)
}
And then:
scala> def forRange[A](range: Range)(func: Int => A) = macro _forRange[A]
defined term macro forRange: [A](range: Range)(func: Int => A)Unit
scala> forRange(0 to 10) { i => println(i) }
0
1
2
3
4
5
6
7
8
9
10
In general, when you're grabbing a tree from one place and plopping it somewhere else, it's likely going to be necessary to use resetAllAttrs to get all the symbols right.
Oscar Boykin pointed out on Twitter that my previous answer no longer works, and it wasn't a very complete answer anyway—it addresses the problem pointed out by the OP on Scala 2.10, but it's not careful about hygiene—if you wrote iter => println(iter) you'd get a compile-time failure, for example.
A better implementation for 2.11 would use a Transformer to rewrite the tree after un-typechecking it:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
def _forRange[A](c: Context)(r: c.Expr[Range])(f: c.Expr[Int => A]): c.Tree = {
import c.universe._
f.tree match {
case q"($i: $_) => $body" =>
val newName = TermName(c.freshName())
val transformer = new Transformer {
override def transform(tree: Tree): Tree = tree match {
case Ident(`i`) => Ident(newName)
case other => super.transform(other)
}
}
q"""
val iter = ${r.tree}.iterator
while (iter.hasNext) {
val $newName = iter.next
${ transformer.transform(c.untypecheck(body)) }
}
"""
}
}
def forRange[A](r: Range)(f: Int => A): Unit = macro _forRange[A]
Which works like this:
scala> forRange(0 to 10)((i: Int) => println(i))
0
1
2
3
4
5
6
7
8
9
10
Now it doesn't matter what variable name we use in our function literal, since it'll just be replaced with a fresh variable anyway.