Why is the following code does not work, and how can I overcome it using Iterator?
def f(str : String) : (String, String) = {
str.splitAt(1)
}
var with_id : Iterator[(String, Int)] = List(("test", 1), ("list", 2), ("nothing", 3), ("else", 4)).iterator
println(with_id.mkString(" "))
val result = with_id map { (s : String, i : Int) => (f(s), i) }
println(result.mkString(" "))
Expected output is:
(("t", "est"), 1) (("l", "ist"), 2) ...
Error:
Error:(28, 54) type mismatch;
found : (String, Int) => ((String, String), Int)
required: ((String, Int)) => ?
val result = with_id map { (s : String, i : Int) => (f(s), i) }
^
The problem is that (s : String, i : Int) => (f(s), i) is a Function2 (i.e a function that takes 2 arguments):
scala> (s : String, i : Int) => (f(s), i)
res3: (String, Int) => ((String, String), Int) = <function2>
whereas .map expects a Function1 (taking a tuple as its argument).
You could define a Function1 with
scala> val g = (t: (String, Int)) => (f(t._1), t._2)
g: ((String, Int)) => ((String, String), Int) = <function1>
scala> val result = with_id map g
result: Iterator[((String, String), Int)] = non-empty iterator
But it seems much better (to me at least) to use the more idiomatic pattern matching anonymous function (note the added case) :
scala> val result = with_id map { case (s : String, i : Int) => (f(s), i) }
result: Iterator[((String, String), Int)] = non-empty iterator
with_id.map expects a ((String, Int) => ?) function as input. That is, a function that takes a Tuple as input, not two parameters.
You can use it like this:
with_id map{ case (s,i) => (f(s), i)} //match the input tuple to s and i
Related
I have example code:
def test = {
val l : Seq[(String, String)] = Seq()
val foo : (String, String) => Unit = {case (a, b)=>}
l.foreach[Unit](foo)
}
It gives me the next error:
Error:(8, 21) type mismatch;
found : (String, String) => Unit
required: ((String, String)) => Unit
l.foreach[Unit](foo)
As far as I understand foreach has a type (A=>U)=>Unit where:
A is template parameter of my collection and
U is template parameter of foreach itself
My question is why is ((String, String)) => Unit required? Where do the extra brackets come from?
If you check in the REPL, you'll see that foo is a <function2>, that is, a function that takes 2 argument:
scala> val foo : (String, String) => Unit = {case (a, b)=>}
foo: (String, String) => Unit = <function2>
.foreach however expects a function that takes a single arguement (of type A), which in your case is a Tuple2.
If you set foo to be a <function1>, then it works:
scala> val foo : ((String, String)) => Unit = {case (a, b)=>}
foo: ((String, String)) => Unit = <function1>
scala> val l : Seq[(String, String)] = Seq()
l: Seq[(String, String)] = List()
scala> l.foreach[Unit](foo)
scala>
Let's start from top.
You have
val l: Seq[(String, String)]
that is a Seq of tuples of two strings. So each of the elements of l is of type (String, String) which is a syntactic sugar for Tuple2[String, String].
Now you have
val foo: (String, String) => Unit
that is a function of two arguments each being String. Again, the type signature above is syntactic sugar for Function2[String, String, Unit].
Considering the above your code can be rewritten as follows:
def test = {
val l: Seq[Tuple2[String, String]] = Seq()
val foo: Function2[String, String, Unit] = {case (a, b)=>}
l.foreach[Unit](foo)
}
foreach expects a single argument function, i.e. a Function1[T, R], thus the type mismatch.
The type definition foo: (String, String) => Unit says that foo is a function which takes two parameters of type String. However, your sequence l: Seq[(String, String)] contains tuples of type type t = (String, String). Thus, calling l.foreach expects a function which is applicable to the tuple type t. Thus, it expects a function of type t => Unit, which is equivalent to ((String, String)) => Unit.
I am new to Scala. This is the code that I have written.
object Main extends App {
val mp: Map[String, String] = Map[String, String]("a"->"a", "b"->"b", "c"->"c", "d"->"d")
val s: Option[(String, String)] = mp.find((a: String, b: String) => {
if(a == "c" && b == "c") {
true
}
else {
false
}
})
println(s)
}
I am getting the following error.
error: type mismatch;
found : (String, String) => Boolean
required: ((String, String)) => Boolean
What am I doing wrong?
You need to change
mp.find((a: String, b: String) =>
to either
mp.find(((a: String, b: String)) =>
or
mp.find( case (a: String, b: String) =>
What you have coded is a function expecting two parameters, but you will only be passing in one, which is a Pair (also called Tuple2). The extra braces and the case keyword are ways of specifying that you are only passing in the one parameter, which is an instance of a Pair.
The problem is that find expects a function that takes a single argument, a Tuple2 in this case and returns a Boolean: ((String, String)) => Boolean. However, what you have there is a function that takes two args a and b, not a tuple (brackets matter): (String, String) => Boolean.
Here is one way to fix it. In this case I use pattern matching to extract arguments:
object Main extends App {
val mp: Map[String, String] = Map[String, String]("a"->"a", "b"->"b", "c"->"c", "d"->"d")
val s: Option[(String, String)] = mp.find{ case(a, b) => a == "c" && b == "c" }
println(s)
}
alternatively you could also do:
val s: Option[(String, String)] = mp.find(t => t._1 == "c" && t._2 == "c")
Either would print:
Some((c,c))
I defined a function foo like
def foo(f : (Int, Int) => Int) = f(1,2) // just calling with some default vals
and can invoke it like
foo(_+_)
But when i try to use the same way to invoke a function that takes IntPair(custom type) as param then i receive the error
error: wrong number of parameters; expected = 1
What is the correct syntax to invoke it
Sample Code
type IntPair = (Int, Int)
def sum(f: (IntPair) => Int): (IntPair) => IntPair = {
def iter(pair: IntPair): IntPair = {
val n = readLine()
print(s"$n+")
if (n != "q") {
val (total, accum) = pair
val p: IntPair = (f(n.toInt, total), accum + 1)
iter(p)
} else {
pair
}
}
iter
}
i can invoke like
val f = sum((p : IntPair) => p._1 + p._2) // i want to use here _ syntax
f((0,0))
but not like
val f = sum((_._1 + _._2)) //gives error
scala> def sum(p: ((Int, Int)) => Int) = p((2, 3))
sum: (p: ((Int, Int)) => Int)Int
scala> sum(Function.tupled(_+_))
res4: Int = 5
In Scala parameter lists and tuples are not unified, thus you can't simply pass a tuple to a function that expects multiple arguments.
sum(_._1 + _._2) means sum((x,y) => x._1 + y._2) btw, hence the error.
Dеar Scala,
scala> val f1: ((Int, Int)) => Int = { case (a, b) => a + b }
f1: ((Int, Int)) => Int = <function1>
scala> val f2: (Int, Int) => Int = { case (a, b) => a + b }
f2: (Int, Int) => Int = <function2>
huh?!
scala> f1(1, 2)
res2: Int = 3
Ok...
scala> def takesIntInt2Int(fun: (Int, Int) => Int) = fun(100, 200)
takesIntInt2Int: (fun: (Int, Int) => Int)Int
scala> def takesTuple2Int(fun: ((Int, Int)) => Int) = fun(100, 200)
takesTuple2Int: (fun: ((Int, Int)) => Int)Int
scala> takesIntInt2Int(f2)
res4: Int = 300
scala> takesIntInt2Int(f1)
<console>:10: error: type mismatch;
found : ((Int, Int)) => Int
required: (Int, Int) => Int
takesIntInt2Int(f1)
^
scala> takesTuple2Int(f1)
res6: Int = 300
scala> takesTuple2Int(f2)
<console>:10: error: type mismatch;
found : (Int, Int) => Int
required: ((Int, Int)) => Int
takesTuple2Int(f2)
Right. And now, look at this!
scala> takesTuple2Int { case (a, b, c) => a + b + c }
<console>:9: error: constructor cannot be instantiated to expected type;
found : (T1, T2, T3)
required: (Int, Int)
takesTuple2Int { case (a, b, c) => a + b + c }
^
scala> takesIntInt2Int { case (a, b, c) => a + b + c }
<console>:9: error: constructor cannot be instantiated to expected type;
found : (T1, T2, T3)
required: (Int, Int)
takesIntInt2Int { case (a, b, c) => a + b + c }
Like, srsly? o_O Both result in required: (Int, Int) error.
Why then use case at all in such anonymous functions?
See section 8.5 of the Scala reference (http://www.scala-lang.org/files/archive/nightly/pdfs/ScalaReference.pdf). The expression { case (a, b) => a + b } is interpreted differently based on the expected type. In your definition of f1 it created a PartialFunction[(Int, Int), Int] which was cast to a Function1[(Int, Int), Int], i.e. ((Int, Int)) => Int whereas in the definition of f2 it created a Function2[Int, Int, Int], i.e. (Int, Int) => Int.
These two interpretations relate to the two situations where you would commonly use case in an anonymous function.
One is for writing anonymous functions that accept tuples and work on their components, as you did with f1. An example would be the function you pass to the foreach or map method on a Map, e.g. Map(1 -> 2, 3 -> 4) map { case (k, v) => k + v }.
The second is for writing an anonymous function that does a match on its sole parameter. Your f2 is doing this, but not in any useful way. An example would be the anonymous function passed to collect, e.g. List(1, -2, 3) collect { case x if x > 0 => -x }.
Note that the two can be combined, that is functions like f1 can do complex matching as well. For example, Map(1 -> 2, 3 -> 4) collect { case (k, v) if k < 2 => v }.
Edit: res2 works because of tupling. If an application doesn't type check, the compiler will try wrapping the args in a tuple before failing.
But that is tried just for applications; it's not a general conversion, as you discovered. It will not try to upgrade a value Function2[A, B, C] to Function1[(A, B), C].
Is there a way to destructure input parameters of a function in Scala (akin to Clojure)?
So, instead of
scala> def f(p: (Int, Int)) = p._1
f: (p: (Int, Int))Int
I'd like to have this (it doesn't work):
scala> def f((p1, p2): (Int, Int)) = p1
I guess in scala you would use pattern matching to achieve the same, e.g. like this:
val f: (Int, Int) => Int = { case (p1, p2) => p1 }
Or, equivalently:
def f(p: (Int, Int)) = p match { case(p1, p2) => p1 }
If the types can be inferred, the (Int, Int) => Int can be dropped:
List((1, 2), (3, 4)) map { case (p1, p2) => p1 }
def f(p: ((Int, Int), (Int, Int))) = p._1 > f: (p: ((Int, Int), (Int, Int)))(Int, Int)
f((1,2), (3,4)) > res1: (Int, Int) = (1,2)