Scala - foldLeft type inference fail - scala

In my code, I have the following:
type Occurrences = List[(Char, Int)]
def subtract(x: Occurrences, y: Occurrences): Occurrences = {
val yMap = y.toMap
x foldLeft (List[(Char, Int)]()) { // ERROR
case (a: List[(Char, Int)], xe: (Char, Int)) =>
if(yMap.contains(xe._1)) (xe._1 -> (xe._2 - yMap(xe._1))) :: a
else a
}
}
It fails on compile-time, at the { that is right before the error mark in the code. The error message is the following:
missing parameter type for expanded function The argument types of an
anonymous function must be fully known. (SLS 8.5) Expected type was:
Int
1) How could that be? As far as I can see, there is no room for misinterpretation of type information here, and I find a lot of such examples over the internet. How can I fix that?
2) Why is it thinking that the expected type is Int after all?

The error occurs because you wrote xs foldLeft (init) (f) instead of (xs foldLeft init)(f) or xs.foldLeft(init)(f).
The former does not work, because Scalas operator notation rules allow only to leave the dot and parentheses if a call occurs in form obj method param, which is not the case with foldLeft because it has two parameter lists.

Related

Strange Scala 'Type mismatch' error for tuples

I have a function map which takes a Parser and a function defined as follows:
def map[T1, T2](parser: Parser[T1], func: T1 => T2): Parser[T2]
I've created a Parser object of type [(Char, Char)] and a function (Char, Char) => String.
val parser[(Char,Char)] = //...
val asString: (Char, Char) => String = (a: Char, b: Char) => a.toString + b.toString
And then I pass these two to the map function.
val mParser: Parser[String] = map(parser, asString)
I expect everything to work fine but I get a type mismatch error for asString argument saying
Error:(26, 41) type mismatch;
found : (Char, Char) => String
required: ((Char, Char)) => String
map[(Char, Char), String](parser, asString)
I have tried to explicitly specify the types for map as map[(Char, Char), String](parser, asString) but that didn't help either.
The type T1 here is the char tuple (Char, Char) and T2 is a String. So, the function (Char, Char) => String is what's supposed to be the input but scala is expecting a different type.
What am I missing here? Why is it expecting ((Char, Char)) => String instead of (Char,Char) => String?
I'm using Scala 2.12. Don't know if that is relevant in any way.
Your help is appreciated.
The type (Char, Char) => String corresponds to a function that takes two Char parameters and returns a String.
What you want is a function that takes a Tuple2 and returns a String which is somehow different.
Its type should be Tuple2[Char, Char] => String.
Tuple2[Char, Char] corresponds to the type shorthand (Char, Char) but I guess during function definition the compiler interprets the parentheses as if they are used to group the function parameters.
This is a known issue and it's being addressed in scala3.
https://dotty.epfl.ch/docs/reference/auto-parameter-tupling.html
As others pointed out, defining a function, that accepts a Tuple2 rather than two parameters, gets a little tricky, and ugly-looking.
A nice way around that is to use .tupled:
val asString: (Char, Char) => String = (a: Char, b: Char) => a.toString + b.toString
val mParser: Parser[String] = map(parser, asString.tupled)
FunctionN.tupled converts a function accepting N arguments into an equivalent one taking a TupleN.
This is a bit nicer than defining a tuple-taking function, because of the parenthesis quirks you ran into, and also, because you don't have to deconstruct the tuple in the body.

Scala function composition: brackets and types

(1) Having defined two functions in the Scala REPL:
scala> def f(s: String) = "f(" + s + ")"
f: (s: String)String
scala> def g(s: String) = "g(" + s + ")"
g: (s: String)String
(2) Composing them without brackets works as expected:
scala> f _ compose g _
res18: String => String = <function1>
(3) Composing them with brackets doesn't:
scala> f(_).compose(g(_))
<console>:14: error: missing parameter type for expanded function ((x$1) => f(x$1).compose(((x$2) => g(x$2))))
f(_).compose(g(_))
^
<console>:14: error: missing parameter type for expanded function ((x$2) => g(x$2))
f(_).compose(g(_))
^
<console>:14: error: type mismatch;
found : String
required: Int
f(_).compose(g(_))
^
Question 1: Can somebody explain why?
Question 2: Why the type mismatch? Why is Scala expecting an Int at all?
(4) Surrounding f(_) with brackets seems to help a little bit, by making the first two errors go away:
scala> (f(_)).compose(g(_))
<console>:14: error: missing parameter type for expanded function ((x$2) => g(x$2))
(f(_)).compose(g(_))
^
Question 3: Why do these brackets help?
Question 4: Why does Scala need the parameter types, even though they are clearly defined in f and g respectively?
(5) Finally, adding the parameter type makes it work:
scala> (f(_)).compose(g(_:String))
res22: String => String = <function1>
Could you please explain what's going on, and provide alternative syntaxes to achieve the composition?
Thanks.
You can see the (unexpected) expansion using magic show comment:
scala> f(_).compose(g(_)) // show
[snip]
val res0 = ((x$1) => f(x$1).compose(((x$2) => g(x$2))))
Function literals need the params constrained, as you showed. f _ is eta expansion, which is different from f(_) which is sugar for x => f(x).
Since the unintended application f(x$1) returns a string, which is a Int => Char for indexing, you get the added type mismatch.
Underscore is covered by many SO questions, including one canonical.

missing parameter type for `_`

I'm trying to sort a list by how close the entries of a list are to num.
I decided to try use sortWith, but the following snippet:
list.sortWith(math.abs(_ - num) < math.abs(_ - num))
failed with missing parameter type for _ in scala.
list is of type List[Int].
Following the other threads, I know that _ is somehow type ambiguous, but I'm not sure why, (and why the following snippet is not type ambiguous):
scala> val sortedDudes = dudes.sortWith(_.name < _.name)
sortedDudes: Array[Person] = Array(Al, Paul, Tyler)
(Source)
def foo = {
val num = 2
val list: List[Int] = List(1, 2)
list.sortWith((a, b) => math.abs(a - num) < math.abs(b - num))
}
work perfectly. It's because scala trying get _ from math.abs, not sortWith
In Scala, _ can be used in a variety of different situations to mean different things. The answers on this question should help clarify a few of them.
Going back to the question, it seems the OP is trying to use _ for parameter replacement. Consider the following example
List(1,2,5,7).filter(_ > 4)
Here filter takes a function of type A => Unit, so the above is shorthand for
List(1,2,5,7).filter(x => x > 4)
The underscore can stand for more than one parameter, but it must be used to refer to each parameter exactly once. This is why the sortedDudes snippet in the OP works. Therefore the following is legal.
List(1,2,5,7).reduce(_ + _)
which is shorthand for
List(1,2,5,7).reduce((a,b) => a + b)
I think the problem with the original snippet is that the compiler cannot unambiguously parse it into something of type (A, A) => Boolean as required by the sortWith method. We can give the compiler a little help as follows.
scala> def op(int: Int, num: Int) = math.abs(int - num)
op: (int: Int, num: Int)Int
scala> List(1,7,5,10).sortWith(op(_, 5) < op(_, 5))
res0: List[Int] = List(5, 7, 1, 10)

Scala - error when passing parameters

I am doing the Martin Odersky course about Scala.
In one of the assignments I have the following type:
type Occurrences = List[(Char, Int)]
I have defined a method which subtracts an element of type (Char, Int) from an element of type Occurrences.
def subtractOne(x: Occurrences, (char: Char, nr: Int)): Occurrences = x match {
case List() => throw new Exception("can not subtract")
case (char, nr2) :: ocs => {
if(nr2 > nr) (char, nr2 - nr) :: ocs
else if(nr2 == nr) ocs
else throw new Exception("can not subtract")
}
case _ :: ocs => subtractOne(ocs, (char, nr))
}
However, I am getting some unclear errors on the first line: Wrong parameter and Definition or declaration expected.
Is there anything wrong with the way I declared the parameters?
Do not use brackets in parameter list. Unless you want to define tuple but it should be done with one name.
def subtractOne(x: Occurrences, char: Char, nr: Int): Occurrences = x match {
Tuples are defined under one name - charAndNr: (Char, Int)
Also Nil is preferred to List()

Scala reduceByKey function - use any type that has + method

I am writing a simple function called reduceByKey that takes a collection of (key, numeric) pairs and returns reduced collection by key.
def reduceByKey[K](collection: Traversable[Tuple2[K, Int]]) = {
collection
.groupBy(_._1)
.map { case (group: K, traversable) => traversable.reduce{(a,b) => (a._1, a._2 + b._2)} }
}
This currently works for:
scala> val col = List((("some","key"),100), (("some","key"),100), (("some","other","key"),50))
col: List[(Product with Serializable, Int)] = List(((some,key),100), ((some,key),100), ((some,other,key),50))
scala> reduceByKey(col)
res42: scala.collection.immutable.Map[Product with Serializable,Int] = Map((some,key) -> 200, (some,other,key) -> 50)
But, I as soon as I want to use non-Int type for numeric, it fails miserably, as it expects an Int.
scala> val col = List((("some","key"),100.toDouble), (("some","key"),100.toDouble), (("some","other","key"),50.toDouble))
col: List[(Product with Serializable, Double)] = List(((some,key),100.0), ((some,key),100.0), ((some,other,key),50.0))
scala> reduceByKey(col)
<console>:13: error: type mismatch;
found : List[(Product with Serializable, Double)]
required: Traversable[(?, Int)]
reduceByKey(col)
^
Of course, I could make different methods for different types, but that would be silly. Basically I want my method to work with any type that has + method defined. That would be Double, Float, Long, Int and Short.
At first, I thought I could use structural type instead of Int. But that would mean the structural type would need to reference itself in order to be of any use.
I looked into Numeric trait that I think could be useful. It encapsulates the + methods of all numeric types. However, I am not sure how to use it in my case. I dont want force the user of my function to wrap values in Numeric just for my function to work. The function itself should somehow implicitly wrap it up and invoke Numeric.plus.
I am open to any suggestions as how to solve this.
If you are only interested in numeric values, you can use the standard Numeric type class and do this:
def reduceByKey[K,V](collection: Traversable[Tuple2[K, V]])(implicit num: Numeric[V]) = {
import num._
collection
.groupBy(_._1)
.map { case (group: K, traversable) => traversable.reduce{(a,b) => (a._1, a._2 + b._2)} }
}
The num implicit parameter serves as an evidence that V is a numeric type, and provides the + operation for this type.