scala match on iteration, weird compiler error - scala

This small piece of scala code when compiled
it gives a strange error.
import scala.collection.mutable.ListBuffer
object main {
def main(args: Array[String]) {
val rs = new ListBuffer[String]()
val ns = scala.collection.mutable.Map[String, String]()
"A very long string".split("\\ ") foreach { word =>
word match {
case x if x.length() > 7 => ns += x -> "="
case _ => rs += word
}
}
}
}
Gives the following error:
test.scala:11: error: type arguments [String,Iterable[java.io.Serializable] with PartialFunction[String with Int,String] with scala.collection.generic.Subtractable[String,Iterable[java.io.Serializable] with PartialFunction[String with Int,String] with scala.collection.generic.Subtractable[String,Equals]]{def seq: Iterable[java.io.Serializable] with PartialFunction[String with Int,String]}] do not conform to trait Subtractable's type parameter bounds [A,+Repr <: scala.collection.generic.Subtractable[A,Repr]]
"A very long string".split("\\ ") foreach { word =>
^
one error found
Any hints?

This happens due to the fact that your pattern match returns a lengthy compound type:
[String,Iterable[java.io.Serializable] with PartialFunction[String with Int,String] with scala.collection.generic.Subtractable[String,Iterable[java.io.Serializable] with PartialFunction[String with Int,String] with scala.collection.generic.Subtractable[String,Equals]]{def seq: Iterable[java.io.Serializable] with PartialFunction[String with Int,String]}]
Where one of the types in the linearization is Subtractable[String, Iterable[Serializable]] which your type doesn't conform to it's type constraint:
trait Subtractable[A, +Repr <: Subtractable[A, Repr]
To fix this, help the compiler out with type annotation on foreach:
"A very long string"
.split("\\")
.foreach[Unit] {
case x if x.length() > 7 => ns += x -> "="
case word => rs += word
}
As a side note, perhaps you can find the following implementation helpful:
val (largerThanSeven, smallerThanSeven) = "A very long string"
.split("\\")
.partition(_.length > 7)
val largerThanSevenMap = largerThanSeven.map(_ -> "=").toMap

The problem is here:
ns += x -> "="
I guess the intention is to put the key x and the value "=" into the mutable map.
This will compile if changed to
ns(x) = "="
The problem is that there are implicits involved into conversions with -> and the types are inferred weirdly into the monstrous string in the error message.

Related

Providing the equivalent of a type parameter [T] from inside a Scala 3 macro

I'm, um, a very naive Scala 3 metaprogrammer. Apologies in advance.
I'm trying to canonicalize type names. Calling _.dealias.simplified.show on a TypeRepr does the job just fine on the base type, but it doesn't touch the type parameters. So, I'd like to iterate through the type params and call my canonicalizer on them recursively. After some trial and error and reading great intros by Adam Warsky and Eugene Yokota I've managed to iterate through the type params, but I can't figure out how to make the recursive call.
object Playpen:
import scala.quoted.*
inline def recursiveCanonicalName[T]: String = ${Playpen.recursiveCanonicalNameImpl[T]}
def recursiveCanonicalNameImpl[T](using q : Quotes)( using tt : Type[T]) : Expr[String] =
import quotes.reflect.*
val repr = TypeRepr.of[T]
repr.widenTermRefByName.dealias match
case AppliedType(name, args) =>
Expr(name.dealias.simplified.show + "[" + args.map(a => a.dealias.simplified.show /*(recursiveCanonicalNameImpl(q)(a.asType)*/).mkString(",") + "]")
case _ =>
Expr(repr.dealias.simplified.show)
The current version "works" to canonicalize one level of type params, but without recursion can't go deeper.
# macroplay.Playpen.recursiveCanonicalName[Map[String,String]]
res1: String = "scala.collection.immutable.Map[java.lang.String,java.lang.String]"
# macroplay.Playpen.recursiveCanonicalName[Map[Seq[String],Seq[String]]]
res3: String = "scala.collection.immutable.Map[scala.collection.immutable.Seq[scala.Predef.String],scala.collection.immutable.Seq[scala.Predef.String]]"
Any help (and your patience, scalameta makes me feel dumb) is greatly appreciated!
Try pattern matching by type quotation
args.map(a =>
a.asType match {
case '[a] => recursiveCanonicalNameImpl[a].show.stripPrefix("\"").stripSuffix("\"")
}
)
`tq` equivalent in Scala 3 macros
Explicit type conversion in Scala 3 macros
What Scala 3 syntax can match on a Type and its Type parameters in the context of a macro?
Alternatively you can introduce a recursive helper function for TypeRepr argument rather than static type T
def recursiveCanonicalNameImpl[T](using q: Quotes)(using tt: Type[T]): Expr[String] =
import quotes.reflect.*
def hlp(repr: TypeRepr): Expr[String] =
repr.widenTermRefByName.dealias match
case AppliedType(name, args) =>
Expr(name.dealias.simplified.show + "[" + args.map(a =>
hlp(a).show.stripPrefix("\"").stripSuffix("\"")
).mkString(",") + "]")
case _ =>
Expr(repr.dealias.simplified.show)
val repr = TypeRepr.of[T]
hlp(repr)

Syntactic sugar explanation of Scala'a unapply method

I am getting an error in the extractor step (unapply method call).
The error message is: Wrong number of arguments for the extractors. found 2; expected 0
Can someone please help what is causing the error (where my misunderstanding is).
class ABC(val name:String, val age:Int) //class is defined.
object ABC{
def apply(age:Int, name:String) = new ABC(name, age)
def unapply(x:ABC) = (x.name, x.age)
}
val ins = ABC(25, "Joe") //here apply method is in action.
val ABC(x,y) = ins //unapply is indirectly called. As per my understanding , 25 and Joe suppose to be captured in x and y respectively. But this steps gives error.
The error I get is
an unapply result must have a member def isEmpty: Boolean
The easiest way to fix this is to make unapply return an Option:
def unapply(x: ABC) = Option((x.name, x.age))
The unapply method in an extractor which binds values must return an Option. This is because there's no intrinsic guarantee that an extractor will always succeed. For instance consider this massively oversimplified example of an extractor for an email address:
object Email {
def unapply(s: String): Option[(String, String)] =
s.indexOf('#') match {
case idx if idx >= 0 =>
val (user, maybeSite) = s.splitAt(idx)
if (maybeSite.length < 2 || maybeSite.lastIndexOf('#') > 0) None
else Some(user -> maybeSite.tail)
case _ => None
}
}
At the application site:
val Email(u, s) = "user3103957#stackoverflow.example.xyz"
Turns into code that's basically (from the description in Programming In Scala (Odersky, Spoon, Venners (3rd ed))):
val _tmpTuple2 =
"user3103957#stackoverflow.example.xyz" match {
case str: String =>
Email.unapply(str).getOrElse(throw ???)
case _ => throw ???
}
val u = _tmpTuple2._1
val s = _tmpTuple2._2
Technically, since the compiler already knows that the value is a String, the type check is elided, but I've included the type check for generality. The desugaring of extractors in a pattern match also need not throw except for the last extractor attempt.

Convert Seq[Try[Option(String, Any)]] into Try[Option[Map[String, Any]]]

How to conveniently convert Seq[Try[Option[String, Any]]] into Try[Option[Map[String, Any]]].
If any Try before convert throws an exception, the converted Try should throw as well.
Assuming that the input type has a tuple inside the Option then this should give you the result you want:
val in: Seq[Try[Option[(String, Any)]]] = ???
val out: Try[Option[Map[String,Any]]] = Try(Some(in.flatMap(_.get).toMap))
If any of the Trys is Failure then the outer Try will catch the exception raised by the get and return Failure
The Some is there to give the correct return type
The get extracts the Option from the Try (or raises an exception)
Using flatMap rather than map removes the Option wrapper, keeping all Some values and discaring None values, giving Seq[(String, Any)]
The toMap call converts the Seq to a Map
Here is something that's not very clean but may help get you started. It assumes Option[(String,Any)], returns the first Failure if there are any in the input Seq and just drops None elements.
foo.scala
package foo
import scala.util.{Try,Success,Failure}
object foo {
val x0 = Seq[Try[Option[(String, Any)]]]()
val x1 = Seq[Try[Option[(String, Any)]]](Success(Some(("A",1))), Success(None))
val x2 = Seq[Try[Option[(String, Any)]]](Success(Some(("A",1))), Success(Some(("B","two"))))
val x3 = Seq[Try[Option[(String, Any)]]](Success(Some(("A",1))), Success(Some(("B","two"))), Failure(new Exception("bad")))
def f(x: Seq[Try[Option[(String, Any)]]]) =
x.find( _.isFailure ).getOrElse( Success(Some(x.map( _.get ).filterNot( _.isEmpty ).map( _.get ).toMap)) )
}
Example session
bash-3.2$ scalac foo.scala
bash-3.2$ scala -classpath .
Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_66).
Type in expressions for evaluation. Or try :help.
scala> import foo.foo._
import foo.foo._
scala> f(x0)
res0: scala.util.Try[Option[Equals]] = Success(Some(Map()))
scala> f(x1)
res1: scala.util.Try[Option[Equals]] = Success(Some(Map(A -> 1)))
scala> f(x2)
res2: scala.util.Try[Option[Equals]] = Success(Some(Map(A -> 1, B -> two)))
scala> f(x3)
res3: scala.util.Try[Option[Equals]] = Failure(java.lang.Exception: bad)
scala> :quit
If you're willing to use a functional support library like Cats then there are two tricks that can help this along:
Many things like List and Try are traversable, which means that (if Cats's implicits are in scope) they have a sequence method that can swap two types, for example converting List[Try[T]] to Try[List[T]] (failing if any of the items in the list are failure).
Almost all of the container types support a map method that can operate on the contents of a container, so if you have a function from A to B then map can convert a Try[A] to a Try[B]. (In Cats language they are functors but the container-like types in the standard library generally have map already.)
Cats doesn't directly support Seq, so this answer is mostly in terms of List instead.
Given that type signature, you can iteratively sequence the item you have to in effect push the list type down one level in the type chain, then map over that container to work on its contents. That can look like:
import cats.implicits._
import scala.util._
def convert(listTryOptionPair: List[Try[Option[(String, Any)]]]): Try[
Option[Map[String, Any]]
] = {
val tryListOptionPair = listTryOptionPair.sequence
tryListOptionPair.map { listOptionPair =>
val optionListPair = listOptionPair.sequence
optionListPair.map { listPair =>
Map.from(listPair)
}
}
}
https://scastie.scala-lang.org/xbQ8ZbkoRSCXGDJX0PgJAQ has a slightly more complete example.
One way to approach this is by using a foldLeft:
// Let's say this is the object you're trying to convert
val seq: Seq[Try[Option[(String, Any)]]] = ???
seq.foldLeft(Try(Option(Map.empty[String, Any]))) {
case (acc, e) =>
for {
accOption <- acc
elemOption <- e
} yield elemOption match {
case Some(value) => accOption.map(_ + value)
case None => accOption
}
}
You start off with en empty Map. You then use a for comprehension to go through the current map and element and finally you add a new tuple in the map if present.
The following solutions is based on this answer to the point that almost makes the question a duplicate.
Method 1: Using recursion
def trySeqToMap1[X,Y](trySeq : Seq[Try[Option[(X, Y)]]]) : Try[Option[Map[X,Y]]] = {
def helper(it : Iterator[Try[Option[(X,Y)]]], m : Map[X,Y] = Map()) : Try[Option[Map[X,Y]]] = {
if(it.hasNext) {
val x = it.next()
if(x.isFailure)
Failure(x.failed.get)
else if(x.get.isDefined)
helper(it, m + (x.get.get._1-> x.get.get._2))
else
helper(it, m)
} else Success(Some(m))
}
helper(trySeq.iterator)
}
Method 2: directly pattern matching in case you are able to get a stream or a List instead:
def trySeqToMap2[X,Y](trySeq : LazyList[Try[Option[(X, Y)]]], m : Map[X,Y]= Map.empty[X,Y]) : Try[Option[Map[X,Y]]] =
trySeq match {
case Success(Some(h)) #:: tail => trySeqToMap2(tail, m + (h._1 -> h._2))
case Success(None) #:: tail => tail => trySeqToMap2(tail, m)
case Failure(f) #:: _ => Failure(f)
case _ => Success(Some(m))
}
note: this answer was previously using different method signatures. It has been updated to conform to the signature given in the question.

scala parallel set recursive value needs type

I have a code from 'Scala in depth' that works in interactive editor:
(1 to 1000).par map { _ => Thread.currentThread.toString } toSet
This prints a set with threads that were used for this parallel operation. All works great and a result type is ParSet[String]
But then I try to use it in a code and I have this:
val pthr = (1 to 1000).par map { _ => Thread.currentThread.toString } toSet
//val x = pthr.toSet
println("thread = " + pthr)
This throws: " error: recursive value pthr needs type" in println line. Another observation is when I uncomment second line everything works fine and a result is ParSet().
What is going on here? What is a type of variable "pthr"?
The println method expects a string and you've provided a string with a + operator to pthr. In order to convert pthr to a string, the compiler needs to know which type it is. You haven't provided a type in your declaration of pthr but instead let it be inferred. For whatever reason, the inference isn't working so the compiler is requesting an explicit type declaration. You could simply do this:
val pthr : Set[String] = (1 to 1000).par map { _ => Thread.currentThread.toString } toSet
and that should take care of it.
However, I cannot reproduce your results with Scala 2.11.2. I get:
scala> val pthr = (1 to 1000).par map { _ => Thread.currentThread.toString } toSet
<console>:7: warning: postfix operator toSet should be enabled
by making the implicit value scala.language.postfixOps visible.
This can be achieved by adding the import clause 'import scala.language.postfixOps'
or by setting the compiler option -language:postfixOps.
See the Scala docs for value scala.language.postfixOps for a discussion
why the feature should be explicitly enabled.
val pthr = (1 to 1000).par map { _ => Thread.currentThread.toString } toSet
^
pthr: scala.collection.parallel.immutable.ParSet[String] = ParSet(Thread[ForkJoinPool-1-worker-29,5,main], Thread[ForkJoinPool-1-worker-19,5,main], Thread[ForkJoinPool-1-worker-13,5,main], Thread[ForkJoinPool-1-worker-3,5,main], Thread[ForkJoinPool-1-worker-27,5,main], Thread[ForkJoinPool-1-worker-5,5,main], Thread[ForkJoinPool-1-worker-1,5,main], Thread[ForkJoinPool-1-worker-23,5,main], Thread[ForkJoinPool-1-worker-15,5,main], Thread[ForkJoinPool-1-worker-11,5,main], Thread[ForkJoinPool-1-worker-21,5,main], Thread[ForkJoinPool-1-worker-9,5,main], Thread[ForkJoinPool-1-worker-7,5,main], Thread[ForkJoinPool-1-worker-17,5,main], Thread[ForkJoinPool-1-worker-31,5,main], Thread[ForkJoinPool-1-worker-25,5,main])
scala> println("thread = " + pthr)
thread = ParSet(Thread[ForkJoinPool-1-worker-29,5,main], Thread[ForkJoinPool-1-worker-19,5,main], Thread[ForkJoinPool-1-worker-13,5,main], Thread[ForkJoinPool-1-worker-3,5,main], Thread[ForkJoinPool-1-worker-27,5,main], Thread[ForkJoinPool-1-worker-5,5,main], Thread[ForkJoinPool-1-worker-1,5,main], Thread[ForkJoinPool-1-worker-23,5,main], Thread[ForkJoinPool-1-worker-15,5,main], Thread[ForkJoinPool-1-worker-11,5,main], Thread[ForkJoinPool-1-worker-21,5,main], Thread[ForkJoinPool-1-worker-9,5,main], Thread[ForkJoinPool-1-worker-7,5,main], Thread[ForkJoinPool-1-worker-17,5,main], Thread[ForkJoinPool-1-worker-31,5,main], Thread[ForkJoinPool-1-worker-25,5,main])
Other than the use of the toSet method as a postFix operator, there's nothing wrong with the code and it compiles and executes correctly. Perhaps you should check your version of the compiler?
Yes I know this works in interactive scala but doesn't during compilation by scalac.
Okay here is minimalistic example let's call this file "test.scala":
//test.scala
object test {
def main(args: Array[String]) = {
val pthr = (1 to 1000).par map { _ => Thread.currentThread.toString } toSet
println("thread = " + pthr)
}
}
Now let's execute in terminal:
-> scalac test.scala
test.scala:5: error: recursive value pthr needs type
println("thread = " + pthr)
^
one error found
-> scalac -version
Scala compiler version 2.11.2 -- Copyright 2002-2013, LAMP/EPFL
So again I have this issue only during compilation with scalac.
And when I add a type to pthr (val pthr: Set[String] = ...) then an error is:
test.scala:4: error: type mismatch;
found : Boolean
required: Set[String]
val pthr:Set[String] = (1 to 1000).par map { _ => Thread.currentThread.toString } toSet
^
one error found
No idea what is going on :/
BTW. this is the exact code copied from "Depth in scala" page 205.

Warning about an unchecked type argument in this Scala pattern match?

This file:
object Test extends App {
val obj = List(1,2,3) : Object
val res = obj match {
case Seq(1,2,3) => "first"
case _ => "other"
}
println(res)
}
Gives this warning:
Test.scala:6: warning: non variable type-argument A in type pattern Seq[A]
is unchecked since it is eliminated by erasure
case Seq(1,2,3) => "first"
Scala version 2.9.0.1.
I don't see how an erased type parameter is needed to perform the match. That first case clause is meant to ask if obj is a Seq with 3 elements equal to 1, 2, and 3.
I would understand this warning if I had written something like:
case strings : Seq[String] => ...
Why do I get the warning, and what is a good way to make it go away?
By the way, I do want to match against something with static type of Object. In the real code I'm parsing something like a Lisp datum - it might be an String, sequence of datums, Symbol, Number, etc.
Here is some insight to what happens behind the scene. Consider this code:
class Test {
new Object match { case x: Seq[Int] => true }
new Object match { case Seq(1) => true }
}
If you compile with scalac -Xprint:12 -unchecked, you'll see the code just before the erasure phase (id 13). For the first type pattern, you will see something like:
<synthetic> val temp1: java.lang.Object = new java.lang.Object();
if (temp1.isInstanceOf[Seq[Int]]())
For the Seq extractor pattern, you will see something like:
<synthetic> val temp3: java.lang.Object = new java.lang.Object();
if (temp3.isInstanceOf[Seq[A]]()) {
<synthetic> val temp4: Seq[A] = temp3.asInstanceOf[Seq[A]]();
<synthetic> val temp5: Some[Seq[A]] = collection.this.Seq.unapplySeq[A](temp4);
// ...
}
In both cases, there is a type test to see if the object is of type Seq (Seq[Int] and Seq[A]). Type parameters will be eliminated during the erasure phase. Thus the warning. Even though the second may be unexpected, it does make sense to check the type since if object is not of type Seq that clause won't match and the JVM can proceed to the next clause. If the type does match, then the object can be casted to Seq and unapplySeq can be called.
RE: thoredge comment on the type check. May be we are talking about different things. I was merely saying that:
(o: Object) match {
case Seq(i) => println("seq " + i)
case Array(i) => println("array " + i)
}
translates to something like:
if (o.isInstanceOf[Seq[_]]) { // type check
val temp1 = o.asInstanceOf[Seq[_]] // cast
// verify that temp1 is of length 1 and println("seq " + temp1(0))
} else if (o.isInstanceOf[Array[_]]) { // type check
val temp1 = o.asInstanceOf[Array[_]] // cast
// verify that temp1 is of length 1 and println("array " + temp1(0))
}
The type check is used so that when the cast is done there is no class cast exception.
Whether the warning non variable type-argument A in type pattern Seq[A] is unchecked since it is eliminated by erasure is justified and whether there would be cases where there could be class cast exception even with the type check, I don't know.
Edit: here is an example:
object SeqSumIs10 {
def unapply(seq: Seq[Int]) = if (seq.sum == 10) Some(seq) else None
}
(Seq("a"): Object) match {
case SeqSumIs10(seq) => println("seq.sum is 10 " + seq)
}
// ClassCastException: java.lang.String cannot be cast to java.lang.Integer
Declaring the match object outside at least makes it go away, but I'm not sure why:
class App
object Test extends App {
val obj = List(1,2,3) : Object
val MatchMe = Seq(1,2,3)
val res = obj match {
case MatchMe => "first"
case _ => "other"
}
println(res)
}