scala parser ^^^ followed by a function - scala

I came across this piece code of parsing string like 5*5+5
def add_expr: Parser[Expr] =
mult_expr * (
"+" ^^^ { (a: Expr, b: Expr) => Plus(a, b) } |
"-" ^^^ { (a: Expr, b: Expr) => Minus(a, b) } )
According to document, ^^^ says "if the left operand parses successfully, ignore the result and use the value from the right". Then why this piece of code work? The value from the right of ^^^ is a function, not a Expr value.

I was misled by IDEA. The "*" right after mult_expr is not
def * = rep(this)
but
def *[U >: T](sep: => Parser[(U, U) => U]) = chainl1(this, sep)
Indeed "+" ^^^ { (a: Expr, b: Expr) => Plus(a, b) } generates a parser whose type is Parser[(Expr, Expr) => Expr]. Then this parser is used in foldLeft to generate Expr value

Related

Why Type Check Error are not detected when Pattern Matching on Generic Type in Scala

Defining the following structure:
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[A](head: A, tail: List[A]) extends List[A]
and the following function (please note that the second line which is the type check error spot is also an algorithmic mistake for short-circuiting the result, but nonetheless it came up and i wonder why type checking is not spotting it)
def foldRight[A, B] (list: List[A], z: B)(f: (A,B) => B ): B = list match {
case Nil => z
case Cons(0, _) => z // problematic line
case Cons(s, xs) => f(s, foldRight(xs, z) (f))
}
I know scala also suffer from Type Erasure, but i am surprised that in this case, this can't be detected at compilation time ? Cons is Cons[A] in this case, and z is clearly of type B ?
Any reason why such code actually compile ?
EDIT1
Sounds like there is a lengthy explanation here https://gist.github.com/jkpl/5279ee05cca8cc1ec452fc26ace5b68b, but Long read. If someone could make it simpler :)
Why these or those decisions about language design were made is opinion based.
In Scala spec it's written:
https://scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#type-parameter-inference-for-constructor-patterns
8.3.2 Type parameter inference for constructor patterns
Assume a constructor pattern 𝐶(𝑝1,…,𝑝𝑛) where class 𝐶 has type
parameters 𝑎1,…,𝑎𝑛. These type parameters are inferred in the same
way as for the typed pattern (_: 𝐶[𝑎1,…,𝑎𝑛]).
So for pattern Cons(h, t) type parameter A in Cons[A](h, t) is inferred as if the pattern were _: Cons[A] (which becomes _: Cons[_] at runtime because of erasure).
There is example there in the spec how types are inferred for
class Term[A]
class Number(val n: Int) extends Term[Int]
def f[B](t: Term[B]): B = t match {
case y: Number => y.n
}
It's explained there why for the pattern y: Number type parameter B (new type parameter B) is inferred Int.
Similarly, in our case for the pattern Cons(0, _) (as if it were _: Cons[A]) A is inferred Int.
After typer phase (scalacOptions ++= Seq("-Xprint:typer", "-Xprint-types")) the code becomes
def foldRight[A, B](list: App.List[A], z: B)(f: (A, B) => B): B = list{App.List[A]} match {
case App.this{App.type}.Nil{App.Nil.type} => z{B}
case (head: A, tail: App.List[A]): App.Cons[?A1](0{Int(0)}, _{App.List[A]}){App.Cons[?A1]} => z{B}
case (head: A, tail: App.List[A]): App.Cons[?A2]((s # _{A}){A}, (xs # _{App.List[A]}){App.List[A]}){App.Cons[?A2]} => f.apply{(v1: A, v2: B): B}(s{A}, App.this{App.type}.foldRight{[A, B](list: App.List[A], z: B)(f: (A, B) => B): B}[A, B]{(list: App.List[A], z: B)(f: (A, B) => B): B}(xs{App.List[A]}, z{B}){(f: (A, B) => B): B}(f{(A, B) => B}){B}){B}
}{B}
and after erasure (scalacOptions += "-Xprint:erasure") it becomes
def foldRight(list: App$List, z: Object, f: Function2): Object = {
<synthetic> var rc9: Boolean = false;
<synthetic> <stable> var x2: App$Cons = (null: App$Cons);
{
case <synthetic> val x1: App$List = list;
case11(){
if (App$Nil.==(x1))
matchEnd10(z)
else
case12()
};
case12(){
if (x1.$isInstanceOf[App$Cons]())
{
rc9 = true;
x2 = (x1.$asInstanceOf[App$Cons](): App$Cons);
{
<synthetic> val p3: Object = x2.head();
if (scala.Int.box(0).==(p3))
matchEnd10(z)
else
case13()
}
}
else
case13()
};
case13(){
if (rc9)
{
val s: Object = x2.head();
val xs: App$List = x2.tail();
matchEnd10(f.apply(s, App.this.foldRight(xs, z, f)))
}
else
case14()
};
case14(){
matchEnd10(throw new MatchError(x1))
};
matchEnd10(x: Object){
x
}
}
};
Actually, code similar to yours compiles even in Haskell if we add the information that a type parameter a belongs to type classes Eq and Num to the context of function signature
https://ideone.com/qqOsI2
data List a = Nil | Cons a (List a)
foldRight :: (Eq a, Num a) => List a -> b -> (a -> b -> b) -> b
foldRight list z f = case list of
Nil -> z
Cons 0 _ -> z
Cons s xs -> f s (foldRight xs z f)
Type classes are not first-class citizens in Scala. So Scala can't request similar thing about something like Num (in Scala everything can be compared with ==, so Eq is "automatical").
Cons is Cons[A] in this case
This is not correct, or at very least it is confusing. There are two types A here: the type parameter in Cons[A] and the type parameter in foldRight[A,B]. There is nothing to say that they are the same type. You could equally well define
case class Cons[Z](head: Z, tail: List[Z]) extends List[Z]
So on the problematic line
case Cons(0, _) => z // problematic line
the compiler treats this as Cons[Int](0, _) because the type of head is Int. The compiler is promiscuous in this case and allows this match in all cases even though it can only succeed if foldLeft is called with a List[Int].

Why is the return type of the fold operation Serializable and not String

Here is a simple scala code
scala> val x = scala.collection.immutable.TreeMap[String, String]("a"->"a", "b"->"b")
x: scala.collection.immutable.TreeMap[String,String] = Map(a -> a, b -> b)
scala> val y = x.fold(""){case (acc: String, (k: String, v: String)) => acc + s""", "$k":"$v""""}
y: java.io.Serializable = , "a":"a", "b":"b"
Why is the return type of y not String but java.io.Serializable?
I thought it could be because I am using pattern matching and the match can be non-exhaustive. So I changed my code to
scala> val y = x.fold(""){case (acc: String, (k:String, v:String)) => acc + s""", "$k":"$v"""" case _ => ""}
y: java.io.Serializable = , "a":"a", "b":"b"
The signature of fold is:
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1
Both operands of the the folding operation (op) must be of the same type, but in your case they are String and (String, String), so the compiler instead tries to look for the least-upper bound between the two types, and finds Serializable.
foldLeft and foldRight allow the accumulator and next element to be different types, so those can work.
scala> x.foldLeft("") { case (acc, (k, v)) => acc + s""", "$k":"$v"""" }
res8: String = , "a":"a", "b":"b"
Folding isn't really what you want here, though, because you'll end up with that extra comma unless you handle it specially. Instead, you could use map and mkString.
scala> x.map { case (k, v) => s""""$k":"$v"""" }.mkString(", ")
res10: String = "a":"a", "b":"b"
I also removed the type ascriptions, as they were not necessary.

How can I compose generic methods in Scala?

I'm learning Scala and have the following toy code:
object M {
def isSorted[X](xs: Array[X], compare: (X, X) => Boolean): Boolean =
xs.dropRight(1).zip(xs.drop(1)).forall(Function.tupled(compare))
def curry[A,B,C](f: (A, B) => C) : A => (B => C) =
(a) => (b) => f(a, b)
}
My goal is to call it like:
M.curry(M.isSorted)(Array(1,2,3))((a, b) => a < b)
But I receive the error:
scala> M.curry(M.isSorted)(Array(1,2,3))
<console>:8: error: type mismatch;
found : Int(1)
required: Nothing
M.curry(M.isSorted)(Array(1,2,3))
Let's back up and look at the type of the curried function:
scala> M.curry(M.isSorted)
res2: Array[Nothing] => (((Nothing, Nothing) => Boolean) => Boolean) = <function1>
That's no good. It wants an Array[Nothing] but there can be no instances of Nothing.
I understand at some point I will need to introduce a constraint so the compiler can prove that the expression a < b is allowable; that is, that a and b are Ordered. But I don't know where I would put the constraint. curry is perfectly generic; the constraint doesn't belong there. isSorted knows nothing about the implementation of compare, so it doesn't belong there either.
The closest I've gotten to having it working is with def isSorted[X >: Any]
scala> M.curry(M.isSorted)(Array(1,2,3))((a, b) => a < b)
<console>:8: error: value < is not a member of Any
M.curry(M.isSorted)(Array(1,2,3))((a, b) => a < b)
^
scala> M.curry(M.isSorted)(Array(1,2,3))((a: Int, b: Int) => a < b)
<console>:8: error: type mismatch;
found : (Int, Int) => Boolean
required: (Any, Any) => Boolean
M.curry(M.isSorted)(Array(1,2,3))((a: Int, b: Int) => a < b)
^
How can I get this working?
object M {
def isSorted[X](xs: Array[X], compare: (X, X) => Boolean): Boolean =
xs.dropRight(1).zip(xs.drop(1)).forall(Function.tupled(compare))
def curry[A,B,C](f: (A, B) => C) : A => (B => C) =
(a) => (b) => f(a, b)
}
Well... I don't think this is the best way to define these functions but in this case, since these are generic functions so you need to provide them with a type when calling.
As #dk14 has pointed out in his comment... M.sorted can not get type info because Scala lacks support for polymorphic lambdas.... which simply means no generics in anonymous functions.
So you need to do this for this particular case.
M.curry( M.isSorted[Int] )( Array(1,2,3) ) ( (a , b) => a < b )

Using Tuples in map, flatmap,... partial functions

If I do:
val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map { t => t._1 + t._2 }
It's ok.
If I do:
val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map { case (b, n) => b + n }
It's ok too.
But if I do:
val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map { (b, n) => b + n }
It will not work.
Why should I use "case" keyword to use named tuples?
The error message with 2.11 is more explanatory:
scala> l map { (b, n) => b + n }
<console>:9: error: missing parameter type
Note: The expected type requires a one-argument function accepting a 2-Tuple.
Consider a pattern matching anonymous function, `{ case (b, n) => ... }`
l map { (b, n) => b + n }
^
<console>:9: error: missing parameter type
l map { (b, n) => b + n }
^
For an apply, you get "auto-tupling":
scala> def f(p: (Int, Int)) = p._1 + p._2
f: (p: (Int, Int))Int
scala> f(1,2)
res0: Int = 3
where you supplied two args instead of one.
But you don't get auto-untupling.
People have always wanted it to work that way.
This situation can be understand with the types of inner function.
First, the type syntax of parameter function for the map function is as follows.
Tuple2[Int,Int] => B //Function1[Tuple2[Int, Int], B]
The first parameter function is expand to this.
(t:(Int,Int)) => t._1 + t._2 // type : Tuple2[Int,Int] => Int
This is ok. Then the second function.
(t:(Int, Int)) => t match {
case (a:Int, b:Int) => a + b
}
This is also ok. In the failure scenario,
(a:Int, b:Int) => a + b
Lets check the types of the function
(Int, Int) => Int // Function2[Int, Int, Int]
So the parameter function type is wrong.
As a solution, you can convert multiple arity functions to tuple mode and backward with the helper functions in Function object. You can do following.
val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map(Function.tupled((b, n) => b + n ))
Please refer Function API for further information.
The type of a function argument passed to map function applied to a sequence is inferred by the type of elements in the sequence. In particular,
scenario 1: l map { t => t._1 + t._2 } is same as l map { t: ((String, String)): (String) => t._1 + t._2 } but shorter, which is possible because of type inference. Scala compiler automatically inferred the type of the argument to be (String, String) => String
scenario 2: you can also write in longer form
l map { t => t match {
case(b, n) => b + n
}
}
scenario 3: a function of wrong type is passed to map, which is similar to
def f1 (a: String, b: String) = a + b
def f2 (t: (String, String)) = t match { case (a, b) => a + b }
l map f1 // won't work
l map f2

how to use > < <= >= as functions?

I need define some case classes like the following one:
case class Gt(key: String, value: Any) extends Expression {
def evalute[V, E](f: String => Any) = {
def compare(v: Any): Boolean = {
v match {
case x: Number => x.doubleValue > value.asInstanceOf[Number].doubleValue
case x: Array[_] => x.forall(a => compare(a))
case x => x.toString > value.toString
}
}
compare(f(key))
}
}
i don't like repeat that for > < >= and <=
i also tried this:
trait Expression {
def evalute[V, E](f: String => Any) = true
def compare(v: Any, value: Any, cp: (Ordered[_], Ordered[_]) => Boolean): Boolean = {
v match {
case x: Number => cp(x.doubleValue, value.asInstanceOf[Number].doubleValue)
case x: Array[_] => x.forall(a => compare(a, value, cp))
case x => cp(x.toString, value.toString)
}
}
}
case class Gt(key: String, value: Any) extends Expression {
def evalute[V, E](f: String => Any) = {
compare(f(key), value, ((a, b) => a > b))
}
}
but that not working :(
error: could not find implicit value for parameter ord: scala.math.Ordering[scala.math.Ordered[_ >: _$1 with _$2]]
compare(f(key), value, ((a, b) => a > b))
Is there a way that pass a operator as a function in scala?
(a, b) => a > b works fine. Your problem is with the types.
What are V and E in evalute[V, E] supposed to be?
You pass it (a, b) => a > b as the parameter cp: (Ordered[_], Ordered[_]) => Boolean. So you have a: Ordered[_] and b: Ordered[_]. Which is the same as a: Ordered[X] forSome {type X} and b: Ordered[Y] forSome {type Y}. With these types, a > b doesn't make sense.
In Scala, those are not operators, but methods. You can lift any method to a function by putting an underscore after it. e.g.
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) Client VM, Java 1.6.0_21).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val f: (Int => Boolean) = 1 <= _
f: (Int) => Boolean = <function1>
scala> (0 to 2).map(f)
res0: scala.collection.immutable.IndexedSeq[Boolean] = Vector(false, true, true)
I'm not familiar with Scala, it does seem to have support for anonymous functions/lambdas: http://www.scala-lang.org/node/133