In the scala console I can do the following without a problem :
scala> val tree = q"def f():MySuperType[(Char,Char)]"
tree: universe.DefDef = def f(): MySuperType[scala.Tuple2[Char, Char]]
scala> val q"def $f():$d" = tree
f: universe.TermName = f
d: universe.Tree = MySuperType[scala.Tuple2[Char, Char]]
scala> val tq"$a[$TheTypeThatIWant]" = d
a: universe.Tree = MySuperType
TheTypeThatIWant: universe.Tree = scala.Tuple2[Char, Char]
And I can get what I want : the content of TheTypeThatIWant
Now If I try to do that inside a quasiquote, I get a match exception and I didn't find a way to get the inner type of an applied type.
My code :
tree match {
case q"{..$body}" =>
body.foreach (_ match {
case q"def $functionName:$type = $osef" =>
val tq"$f[$typ]" = d //I want to get $typ !!
...
}
But all I get is :
exception during macro expansion:
exception during macro expansion:
scala.MatchError: MyMacro.MySuperType[(Char, Char)] (of class scala.reflect.internal.Trees$TypeTree)
at MyMacro$$anonfun$getBasicStructure$1$1.apply(MyMacro.scala:737)
at MyMacro$$anonfun$getBasicStructure$1$1.apply(MyMacro.scala:735)
at scala.collection.immutable.List.foreach(List.scala:383)
at MyMacro$.getBasicStructure$1(MyMacro.scala:735)
at MyMacro$.MyMacro_impl(MyMacro.scala:846)
How can I solve that ?
Thank you
Edit :
The problem is not only with quasiquotes, it bugs even when I work with Trees :
case Block(stats,expr) =>
stats.foreach(_ match {
case DefDef(_,_,_,_,typ,_) =>
typ match {
case AppliedTypeTree(t,args) => //doesnt go there
case TypeApply(t,args) => //doesnt go there
case x:TypeTree => //goes there but can't get any info about the applied type
case _ =>
}
})
Edit2 :
You have to do it that way :
case q"def $name:${d:TypeTree} = $b" =>
d.tpe match {
case TypeRef(x,y,z) => //z is the list of applied types, see scaladoc
case _ =>
}
Well, I guess that's because in the console, by the time you call val tq"$a[$TheTypeThatIWant]" = d , the type of d is actually known, but it's not the case in the macro.
Related
I tried to do collection matching in Scala without using scala.reflect.ClassTag
case class Foo(name: String)
case class Bar(id: Int)
case class Items(items: Vector[AnyRef])
val foo = Vector(Foo("a"), Foo("b"), Foo("c"))
val bar = Vector(Bar(1), Bar(2), Bar(3))
val fc = Items(foo)
val bc = Items(bar)
we can not do this:
fc match {
case Items(x) if x.isInstanceOf[Vector[Foo]]
}
because:
Warning: non-variable type argument Foo in type scala.collection.immutable.Vector[Foo] (the underlying of Vector[Foo]) is unchecked since it is eliminated by erasure
and this:
fc match {
case Items(x: Vector[Foo]) =>
}
but we can do this:
fc match {
case Items(x#(_: Foo) +: _) => ...
case Items(x#(_: Bar) +: _) => ...
}
bc match {
case Items(x#(_: Foo) +: _) => ...
case Items(x#(_: Bar) +: _) => ...
}
As you can see, we are check - is collection Foo + vector or Bar + vector.
And here we are have some problems:
We can do Vector(Foo("1"), Bar(2)), and this is will be match with Foo.
We are still need "val result = x.asInstanceOf[Vector[Bar]]" class casting for result extraction
Is there are some more beautiful way?
Like this:
fc match {
case Items(x: Vector[Foo]) => // result is type of Vector[Foo] already
}
What you're doing here is fundamentally just kind of unpleasant, so I'm not sure making it possible to do it in a beautiful way is a good thing, but for what it's worth, Shapeless's TypeCase is a little nicer:
case class Foo(name: String)
case class Bar(id: Int)
case class Items(items: Vector[AnyRef])
val foo = Vector(Foo("a"), Foo("b"), Foo("c"))
val bar = Vector(Bar(1), Bar(2), Bar(3))
val fc = Items(foo)
val bc = Items(bar)
val FooVector = shapeless.TypeCase[Vector[Foo]]
val BarVector = shapeless.TypeCase[Vector[Bar]]
And then:
scala> fc match {
| case Items(FooVector(items)) => items
| case _ => Vector.empty
| }
res0: Vector[Foo] = Vector(Foo(a), Foo(b), Foo(c))
scala> bc match {
| case Items(FooVector(items)) => items
| case _ => Vector.empty
| }
res1: Vector[Foo] = Vector()
Note that while ClassTag instances can also be used in this way, they don't do what you want:
scala> val FooVector = implicitly[scala.reflect.ClassTag[Vector[Foo]]]
FooVector: scala.reflect.ClassTag[Vector[Foo]] = scala.collection.immutable.Vector
scala> fc match {
| case Items(FooVector(items)) => items
| case _ => Vector.empty
| }
res2: Vector[Foo] = Vector(Foo(a), Foo(b), Foo(c))
scala> bc match {
| case Items(FooVector(items)) => items
| case _ => Vector.empty
| }
res3: Vector[Foo] = Vector(Bar(1), Bar(2), Bar(3))
…which will of course throw ClassCastExceptions if you try to use res3.
This really isn't a nice thing to do, though—inspecting types at runtime undermines parametricity, makes your code less robust, etc. Type erasure is a good thing, and the only problem with type erasure on the JVM is that it's not more complete.
If you want something that is simple using implicit conversions. then try this!
implicit def VectorConversionI(items: Items): Vector[AnyRef] = items match { case x#Items(v) => v }
Example:
val fcVertor: Vector[AnyRef] = fc // Vector(Foo(a), Foo(b), Foo(c))
val bcVertor: Vector[AnyRef] = bc // Vector(Bar(1), Bar(2), Bar(3))
What is the most concise way to get 10 out of Some(Some(Some(Some(10))))?
without resorting to some external library like Scalaz.
import scala.language.higherKinds
case class Flattener[W[_], WW, T](fn : WW => W[T])
implicit def optionRecFlattenFn[WW, T](
implicit f : Flattener[Option, WW, T] = Flattener((ww: WW) => Option(ww))
) = Flattener((ww : Option[WW]) => ww.flatMap(f.fn))
def optionRecursiveFlatten[WW, T](www : Option[WW])(
implicit f : Flattener[Option, Option[WW], T]
) = f.fn(www)
val nestedOption = Option(Option(Option(Option(10))))
// Some(Some(Some(Some(10))))
val flatOption = optionRecursiveFlatten(nestedOption)
// Some(10)
val evenMoreNestedOption = Option(Option(Option(Option(Option(Option(10))))))
// Some(Some(Some(Some(Some(Some(10))))))
val flatOption2 = optionRecursiveFlatten(evenMoreNestedOption)
// Some(10)
In case you don't know the nesting level in advance, this will work for any level:
def unwrapRec(o: Option[Any]) : Any = o match {
case Some(inner # Some(_)) => unwrapRec(inner)
case Some(x) => x
case _ => ???
}
However, please note that an Any is returned. You can change it to whatever type you like and adapt the pattern match accordingly but I think you will have to pass in an Option[Any]. So it's not typesafe at this point.
You can use flatten on nested Option[Option[A]] repeatedly:
scala> val a = Some(Some(Some(Some(10))))
a: Some[Some[Some[Some[Int]]]] = Some(Some(Some(Some(10))))
scala> a.flatten.flatten.flatten.get
res0: Int = 10
I do not think there is any generic and type-safe way to extract A from T[A] := Option[T[A]] | A.
Edit: This method can dynamically get the contents, returns either the innermost value, or None:
def unwrap(x: Any): Any = x match {
case Some(v) => unwrap(v)
case v => v
}
scala> unwrap(Some(Some(Some(Some(10)))))
res1: Any = 10
scala> unwrap(Some(None))
res2: Any = None
It has unfortunately too broad type: Any => Any.
I have a match statement like this:
val x = y match {
case array: Array[Float] => call z
case array: Array[Double] => call z
case array: Array[BigDecimal] => call z
case array: Array[_] => show error
}
How do I simplify this to use only two case statements, since first three case statements do same thing, instead of four.
Type erasure does not really gives you opportunity to understand how array was typed. What you should do instead is to extract head ( first element) of array and check it's type. For example following code works for me:
List(1,2,3) match {
case (a:Int) :: tail => println("yep")
}
This work, although not very nice:
def x(y: Array[_]) = y match {
case a if a.isInstanceOf[Array[Double]] ||
a.isInstanceOf[Array[Float]] ||
a.isInstanceOf[Array[BigDecimal]] => "call z"
case _ => "show error"
}
Would have thought that pattern matching with "|" as below would do the trick. However, this gives pattern type is incompatible with expected type on Array[Float] and Array[BigDecimal]. It might be that matching of generic on this single case where it could work has not been given so much attention:
def x(y: Array[_ <: Any]) = y match {
case a # (_:Array[Double] | _:Array[Float] | _:Array[BigDecimal]) => "call z"
case a: Array[_] => "show error"
}
May be it helps a bit:
import reflect.runtime.universe._
object Tester {
def test[T: TypeTag](y: Array[T]) = y match {
case c: Array[_] if typeOf[T] <:< typeOf[AnyVal] => "hi"
case c: Array[_] => "oh"
}
}
scala> Tester.test(Array(1,2,3))
res0: String = hi
scala> Tester.test(Array(1.0,2.0,3.0))
res1: String = hi
scala> Tester.test(Array("a", "b", "c"))
res2: String = oh
You can obtain the class of array elements as follows (it will be null for non-array types): c.getClass.getComponentType. So you can write:
if (Set(classOf[Float], classOf[Double], classOf[BigDecimal]).contains(c.getClass.getComponentType)) {
// call z
} else {
// show error
}
Not particularly Scala'ish, though; I think #thoredge's answer is the best for that.
You could also check whether the Array is empty first and then if not, just pattern match on Array.head...something like:
def x(y: Array[_]) = {
y.isEmpty match {
case true => "error"
case false => y.head match {
case a:Double | a:BigInt => do whatever
case _ => "error"
}
}
}
In Scala we have a by-name-parameters where we can write
def foo[T](f: => T):T = {
f // invokes f
}
// use as:
foo(println("hello"))
I now want to do the same with an array of methods, that is I want to use them as:
def foo[T](f:Array[ => T]):T = { // does not work
f(0) // invokes f(0) // does not work
}
foo(println("hi"), println("hello")) // does not work
Is there any way to do what I want? The best I have come up with is:
def foo[T](f:() => T *):T = {
f(0)() // invokes f(0)
}
// use as:
foo(() => println("hi"), () => println("hello"))
or
def foo[T](f:Array[() => T]):T = {
f(0)() // invokes f(0)
}
// use as:
foo(Array(() => println("hi"), () => println("hello")))
EDIT: The proposed SIP-24 is not very useful as pointed out by Seth Tisue in a comment to this answer.
An example where this will be problematic is the following code of a utility function trycatch:
type unitToT[T] = ()=>T
def trycatch[T](list:unitToT[T] *):T = list.size match {
case i if i > 1 =>
try list.head()
catch { case t:Any => trycatch(list.tail: _*) }
case 1 => list(0)()
case _ => throw new Exception("call list must be non-empty")
}
Here trycatch takes a list of methods of type ()=>T and applies each element successively until it succeeds or the end is reached.
Now suppose I have two methods:
def getYahooRate(currencyA:String, currencyB:String):Double = ???
and
def getGoogleRate(currencyA:String, currencyB:String):Double = ???
that convert one unit of currencyA to currencyB and output Double.
I use trycatch as:
val usdEuroRate = trycatch(() => getYahooRate("USD", "EUR"),
() => getGoogleRate("USD", "EUR"))
I would have preferred:
val usdEuroRate = trycatch(getYahooRate("USD", "EUR"),
getGoogleRate("USD", "EUR")) // does not work
In the example above, I would like getGoogleRate("USD", "EUR") to be invoked only if getYahooRate("USD", "EUR") throws an exception. This is not the intended behavior of SIP-24.
Here is a solution, although with a few restrictions compared to direct call-by-name:
import scala.util.control.NonFatal
object Main extends App {
implicit class Attempt[+A](f: => A) {
def apply(): A = f
}
def tryCatch[T](attempts: Attempt[T]*): T = attempts.toList match {
case a :: b :: rest =>
try a()
catch {
case NonFatal(e) =>
tryCatch(b :: rest: _*)
}
case a :: Nil =>
a()
case Nil => throw new Exception("call list must be non-empty")
}
def a = println("Hi")
def b: Unit = sys.error("one")
def c = println("bye")
tryCatch(a, b, c)
def d: Int = sys.error("two")
def e = { println("here"); 45 }
def f = println("not here")
val result = tryCatch(d, e, f)
println("Result is " + result)
}
The restrictions are:
Using a block as an argument won't work; only the last expression of the block will be wrapped in an Attempt.
If the expression is of type Nothing (e.g., if b and d weren't annotated), the conversion to Attempt is not inserted since Nothing is a subtype of every type, including Attempt. Presumably the same would apply for an expression of type Null.
As of Scala 2.11.7, the answer is no. However, there is SIP-24, so in some future version your f: => T* version may be possible.
How can I use a "match" statement to identify the value of a class variable? The following is invalid, and I can't find an acceptable variant -- other than if ... else if ... else ...
val c: Class[_] = classOf[Int]
val what = c match { case classOf[Int] => "int!"; case classOf[Float] => "float!" }
The compiler complains: error: not found: type classOf
And of course, I can't use Class[Int] because that type information is erased:
c match { case Class[Int] => "int!"; case Class[Float] => "float!" }
error: type Class of type Class does not take type parameters.
I've also tried variants like Int.class, all to no avail. (And I don't really want to convert to strings: I feel it's important to have the compiler catch renamed/moved classes.)
Am I being dense, or have I stumbled into a Scala blind spot?
The verbose case comparison works:
val what = c match {
case q if q == classOf[Int] => "int!"
case q if q == classOf[Float] => "float!"
}
Of course, being a lower-case identifier, classOf should not work directly in a case statement anyway. However, neither does an escaped
case `classOf`[Int]
work in this case, so you’ll have to go with the if-guard.
You can match on class values if you create a stable identifier (ie. a val) for them,
scala> val c: Class[_] = classOf[Int]
c: Class[_] = int
scala> val ClassOfInt = classOf[Int]
ClassOfInt: java.lang.Class[Int] = int
scala> val ClassOfFloat = classOf[Float]
ClassOfFloat: java.lang.Class[Float] = float
scala> val what = c match {
| case ClassOfInt => "int!"
| case ClassOfFloat => "float!"
| }
what: String = int!
Note that you can't match on type (ie. Class[Int]) because erasure means that the different type instantiations of Class[T] are indistinguishable at runtime ... hence the warning below
scala> val what = c match {
| case _: Class[Int] => "int!"
| case _: Class[Float] => "float!"
| }
warning: there were 2 unchecked warnings; re-run with -unchecked for details
what: java.lang.String = int!
I encountered the same problem and placing the class in a 'stable identifier' wasn't that practical. I found the next best thing was to have tidy 'else if' statements.
Using this method:
private def is[T <: AnyRef : Manifest](implicit cls: Class[_]) =
cls == manifest[T].runtimeClass
I can write:
implicit val arg = cls
if (is[ClassA]) ...
else if (is[ClassB]) ...
...
else throw new IllegalArgumentException("Unknown class: " + cls)
To consider inheritance:
val what = c match {
case q if classOf[Int].isAssignableFrom(q) => "int!"
case q if classOf[Float].isAssignableFrom(q) => "float!"
}