I just got puzzled by the fact that this partial function pf doesn't blow up with a MatchError when inner is not InnerA
sealed trait Inner
case class InnerA(name: String) extends Inner
case class InnerB(name: String, value: Int) extends Inner
case class Input(id: String, inner: Inner)
case class Output(id: String, inner: InnerA)
val pf: PartialFunction[Input, Output] = { input =>
input.inner match {
case innerA: InnerA =>
val Input(id, _) = input
Output(id, innerA)
}
}
instead it is simply undefined so this passes
Seq(
Input("1", InnerA("a1")),
Input("2", InnerB("b2", 2)),
Input("3", InnerA("a3"))
).collect(pf) shouldBe Seq(
Output("1", InnerA("a1")),
Output("3", InnerA("a3"))
)
If I add a line I get a compilation warning and trying to pass an InnerB in the collect above throws a MatchError (as I originally expected):
val pf: PartialFunction[Input, Output] = { input =>
println(input)
input.inner match {
case innerA: InnerA =>
val Input(id, _) = input
Output(id, innerA)
}
}
InnerB(b2,2) (of class casa.InnerB)
scala.MatchError: InnerB(b2,2) (of class casa.InnerB)
Why is this? Is this quirk documented somewhere?
(I'm using Scala 2.13.3)
What is happening is that, in the first case, the compiler is "removing" the input match from and using input.inner.
When I run scalac -Xprint:typer Test.scala, the first code turns into:
final override def applyOrElse[A1 <: Input, B1 >: Output](input: A1, default: A1 => B1): B1 = (input.inner: Inner #unchecked) match {
case (innerA # (_: InnerA)) => {
val id: String = (input: A1 #unchecked) match {
case (id: String, inner: Inner): Input((id # _), _) => id
};
Output.apply(id, innerA)
}
case (defaultCase$ # _) => default.apply(input)
};
final def isDefinedAt(input: Input): Boolean = (input.inner: Inner #unchecked) match {
case (innerA # (_: InnerA)) => true
case (defaultCase$ # _) => false
}
Which means that your function will behave like a PartialFunction[Inner, Output], so the compiler knows that it doesn't need to warn you that your match is not exhaustive.
On the other hand, when you see the results for the method with the print instruction, you get:
final override def applyOrElse[A1 <: Input, B1 >: Output](input: A1, default: A1 => B1): B1 = ((input.asInstanceOf[Input]: Input): Input #unchecked) match {
case (defaultCase$ # _) => {
scala.Predef.println("xxx");
input.inner match {
case (innerA # (_: InnerA)) => {
val id: String = (input: A1 #unchecked) match {
case (id: String, inner: Inner): Input((id # _), _) => id
};
Output.apply(id, innerA)
}
}
}
case (defaultCase$ # _) => default.apply(input)
};
final def isDefinedAt(input: Input): Boolean = ((input.asInstanceOf[Input]: Input): Input #unchecked) match {
case (defaultCase$ # _) => true
case (defaultCase$ # _) => false
}
In this case, you're creating a PartialFunction[Input, Output] that is defined for all intervals of Input, and this is fine. But when the compiler checks the inner input.inner match, it warns you that this match - not the first one - is not exhaustive.
this partial function pf doesn't blow up with a MatchError when inner is not InnerA
If you call it directly with such an argument
pf(Input("a", InnerB("b", 0)))
you do get a MatchError; but the whole point of PartialFunction is to provide additional information in isDefinedAt, and collect uses it by not calling pf where isDefinedAt returns false.
Is this quirk documented somewhere?
See paragraph 6.23.1 Translation of specification:
When a PartialFunction is required, an additional member isDefinedAt is synthesized, which simply returns true. However, if the function literal has the shape x => x match { … }, then isDefinedAt is derived from the pattern match in the following way: each case from the match expression evaluates to true, and if there is no default case, a default case is added that evaluates to false.
So for your second version isDefinedAt is always true; the first one doesn't exactly fit x => x match..., but apparently it's supported too. Now the usual way to define a PartialFunction is like Luis Miguel Mejía Suárez's comment says
{ case Input(id, InnerA(name)) => Output(id, InnerA(name)) }
but it simply gets translated into
x => x match { case Input(id, InnerA(name)) => Output(id, InnerA(name)) }
Related
I have the following code,
def obsKPI[T](kpi: Option[T], f: T => Unit) = {
kpi match {
case Some(obsValue) => f(obsValue)
case _ => Unit
}
}
def func1(str:String):Unit = println(str)
def func2(num: Int):Unit = println(num)
//option1: val inputArgs = List((Some("first"),(func1 _)),(Some("third"), (func1 _)))
//option2: val inputArgs = List((Some(456), (func2 _)),(None,(func2 _)))
// option3:
val inputArgs = List((Some("first"),(func1 _)),(Some(456), (func2 _)),(Some("third"), (func1 _)),(None,(func2 _)))
inputArgs.map(x => obsKPI(x._1, x._2))
when running either option 1 or 2 (the inputArgs list contains function of only String=>Unit or Int=>Unit), the code works, but when running option 3, I get an error:
:68: error: type mismatch;
found : Int with String => Unit
required: Any => Unit
inputArgs.map(x => obsKPI(x._1, x._2))
^
Thanks for letting me know what went wrong in here.
Functions aren't covariant in their parameter types (they are in fact contravariant).
This means, that if Foo is a subclass of Bar, Foo => Unit is not a subclass of Bar => Unit (the opposite is true).
In your case, you are trying to coerce func1 and func2 to Any => Unit, but that cannot work, because their types are incompatible - one is String => Unit, and the other one is Int => Unit.
One way to get around this problem, is to use a case class rather than a tuple:
case class KPI[T](t: Option[T], f: T => Unit)
def obsKPI(kpi: KPI[_]) = kpi match {
case KPI(Some(k), f) => f(k)
case _ => () // `()` is the value of type Unit. Unit, as you had it is the value of type Unit.type - not what you want at all
}
// You can also write the above more concise like this: def obsKPI[T](kpi: KPI[T]) = kpi.t.foreach(kpi.f)
def func1(str:String) = println(str)
def func2(num: Int) = println(num)
val inputArgs = List(KPI(Some("first"),func1 _), KPI(Some(456), func2 _), KPI(Some("third"), func1 _), KPI[Int](None,func2 _))
inputArgs.foreach(obsKPI) // Could do .map here too, but ending up with a list of ()s is unlikely what you you want.
You can make it look a bit more elegant, if you make your obsKPI into a member of the case class:
case class KPI[T](t: Option[T], f: T => Unit) {
def obs = t.foreach(f)
}
val inputArgs = List(KPI(Some("first"),func1 _), KPI(Some(456), func2 _), KPI(Some("third"), func1 _), KPI[Int](None,func2 _))
inputArgs.foreach(_.obs)
I'd like to ask where def macros can be called and when they are expanded? I guess we cant just put an appropriate generated AST anywhere it fits?
For example, I want this:
(2,1) match {
case StandaloneMacros.permutations(1,2) => true ;
case (_,_) => false
}
become this after macro expansion
(2,1) match {
case (1,2) | (2,1) => true ;
case (_,_) => false
}
My macro permutations produces an Alternative of tuples. But when I run the first snippet, I get
macro method permutations is not a case class, nor does it have an unapply/unapplySeq member
I also tried defining a Permutations object with unapply macro method but got another error:
scala.reflect.internal.FatalError: unexpected tree: class scala.reflect.internal.Trees$Alternative
So: Is it possible to achieve at all?
I came up with a solution some time ago, and I thought I'd share it with you.
To achieve above task I've used a Transformer and transformCaseDefs
object Matcher {
def apply[A, B](expr: A)(patterns: PartialFunction[A, B]): B = macro apply_impl[A,B]
def apply_impl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(expr: c.Expr[A])(patterns: c.Expr[PartialFunction[A, B]]): c.Expr[B] = {
import c.universe._
def allElemsAreLiterals(l: List[Tree]) = l forall {
case Literal(_) | Ident(_) => true
case _ => throw new Exception("this type of pattern is not supported")
}
val transformer = new Transformer {
override def transformCaseDefs(trees: List[CaseDef]) = trees.map {
case cas # CaseDef(pat # Apply(typTree, argList), guard, body) if allElemsAreLiterals(argList) =>
val permutations = argList.permutations.toList.map(t => q"(..$t)").map {
case Apply(_, args) => Apply(typTree, args)
}
val newPattern = Alternative(permutations)
CaseDef(newPattern, guard, body)
case x => x
}
}
c.Expr[B](q"${transformer.transform(patterns.tree)}($expr)")
}
}
It would be shorter, but somehow you need to provide the same TypeTree which was used in original (before transformation) case statement.
This way, it can be used like this
val x = (1,2,3)
Matcher(x) {
case (2,3,1) => true
case _ => false
}
which is then translated to something like
val x = (1,2,3)
x match {
case (1,2,3) | (1,3,2) | (2,1,3) | (2,3,1) | (3,1,2) | (3,2,1) => true
case _ => false
}
def control(x: String): Option[String] = macro controlImpl
def controlImpl(c: Context)(x: c.Expr[String]): c.Expr[Option[String]] = {
import c.universe._
val result = x.tree match {
case Match(expr, cases) =>
val matchz = Match(q"""List("hello")""", cases)
q"Some(List(5)).map{a => $matchz}"
case a => a
}
c.Expr[Option[String]](result)
}
This macro crashes during macro expansion with the following error:
java.lang.IllegalArgumentException: Could not find proxy for val o7: Some in List(value o7, method apply, <$anon: Function1>, value f, method apply, <$anon: Function1>, method apply, <$anon: Function0>, value <local ApplicativeTest>, object ApplicativeTest, package scalaz, package <root>) (currentOwner= value one )
This is the macro application point:
val f = IdiomBracket.control {
a.get match {
case List(one) => one
case _ => ""
}
}
What is odd is that if you substitute q"Some(List(5)).map{a => $matchz}" with q"Some($matchz)" then the compiler crash goes away.
"Simple macro" is an oxymoron.
The crash is in lambda lift, which indicates a bad symbol owner.
You can inspect the tree you produce with -Ybrowse:typer.
Consider what you'd like to produce:
class X {
//def g = List("hello") match { case List(x) => x case _ => "" }
def f = Some(List(5)).map{a => List("hello") match { case List(x) => x case _ => "" } }
}
The var in the case is owned by the anonfun you want to create:
Symbol: [has] value x
Symbol owner: value $anonfun
Your actual result:
Symbol: [has] value x
Symbol owner: value <local Controlled>
where Controlled is my sample enclosing object for your code.
You might have to rebuild the cases, as opposed to just cases map (_.untypecheck).
https://github.com/scala/scala/blob/v2.11.5/src/reflect/scala/reflect/api/Trees.scala#L1100
In Scala, you can do
list.filter { item =>
item match {
case Some(foo) => foo.bar > 0
}
}
But you can also do the quicker way by omitting match:
list.filter {
case Some(foo) => foo.bar > 0
}
How is this supported in Scala? Is this new in 2.9? I have been looking for it, and I can figure out what makes this possible. Is it just part of the Scala compiler?
Edit: parts of this answer are wrong; please refer to huynhjl's answer.
If you omit the match, you signal the compiler that you are defining a partial function. A partial function is a function that is not defined for every input value. For instance, your filter function is only defined for values of type Some[A] (for your custom type A).
PartialFunctions throw a MatchError when you try to apply them where they are not defined. Therefore, you should make sure, when you pass a PartialFunction where a regular Function is defined, that your partial function will never be called with an unhanded argument. Such a mechanism is very useful e.g. for unpacking tuples in a collection:
val tupleSeq: Seq[(Int, Int)] = // ...
val sums = tupleSeq.map { case (i1, i2) => i1 + i2 }
APIs which ask for a partial function, like the collect filter-like operation on collections, usually call isDefinedAt before applying the partial function. There, it is safe (and often wanted) to have a partial function that is not defined for every input value.
So you see that although the syntax is close to that of a match, it is actually quite a different thing we're dealing with.
The language specification addresses that in section 8.5. The relevant portions:
An anonymous function can be defined by a sequence of cases
{ case p1 => b1 ... case pn => bn }
If the expected type is scala.Functionk[S1, ..., Sk, R] , the expression is taken to
be equivalent to the anonymous function:
(x1 : S1, ..., xk : Sk) => (x1, ..., xk) match {
case p1 => b1 ... case pn => bn
}
If the expected type is scala.PartialFunction[S, R], the expression is taken to
be equivalent to the following instance creation expression:
new scala.PartialFunction[S, T ] {
def apply(x: S): T = x match {
case p1 => b1 ... case pn => bn
}
def isDefinedAt(x: S): Boolean = {
case p1 => true ... case pn => true
case _ => false
}
}
So typing the expression as PartialFunction or a Function influences how the expression is compiled.
Also trait PartialFunction [-A, +B] extends (A) ⇒ B so a partial function PartialFunction[A,B] is also a Function[A,B].
-- Revised post --
Hmm, I'm not sure I see a difference, Scala 2.9.1.RC3,
val f: PartialFunction[Int, Int] = { case 2 => 3 }
f.isDefinedAt(1) // evaluates to false
f.isDefinedAt(2) // evaluates to true
f(1) // match error
val g: PartialFunction[Int, Int] = x => x match { case 2 => 3 }
g.isDefinedAt(1) // evaluates to false
g.isDefinedAt(2) // evaluates to true
g(1) // match error
It seems f and g behave exactly the same as PartialFunctions.
Here's another example demonstrating the equivalence:
Seq(1, "a").collect(x => x match { case s: String => s }) // evaluates to Seq(a)
Even more interesting:
// this compiles
val g: PartialFunction[Int, Int] = (x: Int) => {x match { case 2 => 3 }}
// this fails; found Function[Int, Int], required PartialFunction[Int, Int]
val g: PartialFunction[Int, Int] = (x: Int) => {(); x match { case 2 => 3 }}
So there's some special casing at the compiler level to convert between x => x match {...} and just {...}.
Update. After reading the language spec, this seems like a bug to me. I filed SI-4940 in the bug tracker.
How would you implement class that parses some input via regex and transforms founded string to some other type? My approach is:
class ARegex[T](regex:Regex, reform:Option[String => T]){
def findFirst(input:String):Option[T] = {
(regex.findFirstIn(input), reform) match{
case (None, _) => None
case (Some(s), None) => Some(s) // this won't compile because of type mismatch
case (Some(s), Some(fun)) => Some(fun(s))
}
}
}
class BRegex[T](regex:Regex, reform:Option[String => T]) {
def findFirst(input:String) = { //returns Option[Any] - erasure
(regex.findFirstIn(input), reform) match{
case (None, _) => None
case (Some(s), None) => Some(s)
case (Some(s), Some(fun)) => Some(fun(s))
}
}
}
We can solve this problem by eliminating the Option part of the reform's type, and using a different mechanism to indicate that we don't want to change the match in any way. This mechanism is to use identity as a default parameter or pass identity when you don't want the type to change.
class ARegex[T](regex:Regex, reform:String => T = identity[String](_)){
def findFirst(input:String):Option[T] = {
regex.findFirstIn(input) match{
case None => None
case Some(s) => Some(reform(s))
}
}
}
new ARegex("something".r).findFirst("something else") //returns Option[String]
new ARegex("3".r, {x=>x.toInt}).findFirst("number 3") //returns Option[Int]
Well, the problem is the type mismatch, because you are returning either a String or a T, which, of course, are unified at Any. You can't say you are going to return Option[T] and then return Option[String].
Other than that, a simplified version of that code is this:
class ARegex[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[Any] =
regex findFirstIn input map { s => reform map (_(s)) getOrElse s }
}
You could return an Option[Either[String, T]], though. The code would look like this:
class ARegex[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[Either[String, T]] =
regex findFirstIn input map { s => reform map (_(s)) toRight s }
}
Why is reform Option[String => T] instead of just String => T? If you don't pass in a mechanism for creating an instance of your desired type, there's no mechanism for the runtime system to actually create the appropriate object. If you really need to pass in an Option[String => T] then your second case should simply return None.
Also, flatMap is your friend, and will give you the correct behavior (i.e. if reform is None, the method returns None.
class RegexExtractor[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[T] = reform.flatMap(f => regex.findFirstIn(input).map(f))
}