I am trying to create a map of lambda functions in Scala
val identity = ((x:Any) => x)
val propmap = Map("references-count" -> identity,
"title" -> ((x:List[String]) => x(0)),
"score" -> identity,
"issued" -> ((x:List[Any]) => x(0)))
when I type propmap("score") or propmap("title") the output I get is the same: <function1>.
Running identity(10.1) returns the expected result. However
val f1 = propmap("score")
f1(10.9)
results in:
Name: Unknown Error
Message: <console>:29: error: type mismatch;
found : Double(10.9)
required: List[String]
f1(10.9)
^
StackTrace:
Seemingly the function is overwritten. Where to go for the besung immutabilty?
The problem is the type of propmap.
Scala infers the type as String, (List[String] => Any)
The reason for that is that scala needs to infer a type which matches ALL values. The String as key is obvious but for the function, it needs to find a function type which matches all functions. Since all functions have 1 parameter this would be function1.
If you look at the definition of function1 you would see it is:
trait Function1[-T1, +R] extends AnyRef
This means that it needs to find the first type to be the most constrictive. In this case this is List[String] and therefore it expects a function List[String]=>Any (this actually makes sense because you want a type you can use on ALL functions).
So scala automatically converts your identity function to be (x: List[String] => x) in practice and therefore when you try to pass it a number it fails.
Possible solutions:
The first solution as mentioned by #KotWarm would be to use asInstanceOf:
val f1 = propmap("score").asInstanceOf[Any ⇒ Any]
println(f1(10.9))
The second solution would be to rewrite the functions to use Any, for example:
val propmap = Map("references-count" -> identity,
"title" -> ((x: Any) => x.asInstanceOf[List[String]](0)),
"score" -> identity,
"issued" -> ((x: Any) => x.asInstanceOf[List[Any]](0)))
Because scalac determined the type of collection as
propmap: scala.collection.immutable.Map [String, List [String] => Any]
Determine the type of your collection explicitly so that the compiler knows what you want to get
Here is an example code
val identity = ((x:Any) => x)
val propmap = Map[String,_ => _]("references-count" -> identity,
"title" -> ((x:List[String]) => x(0)),
"score" -> identity,
"issued" -> ((x:List[Any]) => x(0)))
But to execute the methods you must cast the type
val f1 = propmap("score").asInstanceOf[Any ⇒ Any]
println(f1(10.9))
Related
I'm just going over some Scala tutorials on the Internet and have noticed in some examples the author used both => and -> in a HOF(Higher Order Function).
Example:
val vec = Vector("hello", "world").map(s => s -> s.length)
// vec: scala.collection.immutable.Vector[(String, Int)] =
// Vector((hello,5), (world,5))
What is the difference between => and -> in Scala?
-> it used to associate key with a value by using implicit conversion for example tuple, map etc.
scala> 1 -> 2
res0: (Int, Int) = (1,2)
scala> val map = Map("x" -> 12, "y" -> 212)
Its basically just take the item on the left and map it to the item on the right
It converts any type into an instance of "ArrowAssoc" using implicit conversion
implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)
class ArrowAssoc[A](x: A) {
def -> [B](y: B): Tuple2[A, B] = Tuple2(x, y)
}
=> is a syntax provided by the language itself to create instance of function using call-by-name.
It passes value name inside the method for example:
`def f(x:Int => String) {}` function take argument of type Int and return String
You can also pass no argument like below
() => T means it doesn't take any argument but return type T.
-> is used for Method provided by implicit conversion & key->value creates a tuple (key,value).
=> is used for function types, function literals, and import renaming.
Given the
def sum(a: Int, b: Int) = a + b
val add7 = sum(7, _)
// compiler complains missing $x1's type for $x1 => sum(7, $x1)
val add8 = sum(8, _: Int)
// fine
And curried sum can do this without type declaration .
def sum_curry(a: Int)(b: Int) = a + b
val add9 = sum_curry(9)(_)
// fine
But it's not a generic function, what sum's parameter types can be known and it is already there.
Why cannot Scala know the $x1's type from b: Int in sum's signature?
The compiler needs to know the data types of function arguments to map a function call to exactly one function definition (out of many overloaded function definition of with same name)
It will become ambiguous if we try a reverse mapping from function definition to function call to determine the type of a parameter in function call.
Eta expression can convert this to a value type (Int, Int) => Int only when:
_ as in place of the argument of list
val f = sum _
or, you just omit the argument list.
val f :(Int, Int) => Int = sum
and specify its type.
or use each _
val f = sum(_ , _)
As you can see, sum(7, _) cannot satisfy any of above.
I have:
val words = List("all", "the", "words", "all", "the", "counts", "all", "day")
val map = Exercise02.count(words.iterator)
val expected = Map("all" -> 3, "the" -> 2, "words" -> 1, "counts" -> 1, "day" -> 1)
where Exercise02.count is java.util.Iterator[String] => Map[String, Int] and merely produces a count of each word in the input java.util.Iterator[String].
I wrote a test:
object Exercise02Spec extends FlatSpec with Inspectors {
val words = List("all", "the", "words", "all", "the", "counts", "all", "day")
val map = Exercise02.count(words.iterator)
val expected = Map("all" -> 3, "the" -> 2, "words" -> 1, "counts" -> 1, "day" -> 1)
"count" should "count the occurrence of each word" in {
forAll (map) { kv => assert(kv._2 === expected(kv._1)) }
// forAll (map) { (k: String, v: Int) => assert(v === expected(k)) }
}
}
The first line compiles just fine, and the test passes. If I replace the first line with the commented out second line, I get a compilation error.
sbt reports: found : (String, Int) => Unit, required: ((String, Int)) => Unit
IntelliJ IDEA reports: Type mismatch, expected: ((String, Int)) => Unit, actual: (String, Int) => Unit
Why is this? And how do I fix it?
The method you're using accepts a function that transforms a single argument to a single output. What you're telling Scala in your second statement is that map should accept a function with two arguments! There's an important difference between a single argument that just happens to be a tuple of size two, and two distinct arguments.
So you have single argument, but you (and Scala) know it's a tuple. To access those two elements, you have to destructure or pattern match your argument against the tuple you want. You just do that by writing forAll (map) { case (k: String, v: Int) => assert(v === expected(k)) }. You're saying the argument you receive should match the pattern of a tuple (String, Int) and you want the first element bound to k and the second to v. You could probably eliminate mentioning the types explicitly here. Note that this is similar to the syntax used in pattern matching, which is essentially what you're doing here.
In the 2nd example you're defining a two argument function, not a single tuple argument function. Hence why the error is saying it is expecting the double parenthesis around the argument.
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.
I'm not sure why this doesn't work:
scala> case class Loader(n: String, x: String, l: List[String])
scala> val m: Map[String, (List[String])=>Loader] =
| Map("x" -> Loader("x", "x1", _:List[String]))
<console>:8: error: type mismatch;
found : (List[String]) => (java.lang.String, Loader)
required: (String, (List[String]) => Loader)
Map("x" -> Loader("x", "x1", _:List[String]))
but this does?
scala> Loader("t", "x", _:List[String])
res7: (List[String]) => Loader = function1>
scala> val m = Map("x" -> res7)
m: scala.collection.immutable.Map[java.lang.String,(List[String]) => Loader] =
Map((String,function1>))
One more victim of the overload of _ in Scala. Consider this:
f(_, 5) + 1 // Partial function application
f(_ + 1, 5) // Closure
In the first case, _ is replacing the entire parameter. In this case, it stands for a partial application of f. In practice, it's equivalent to x => f(x, 5) + 1, as the whole expression that contains f is turned into a closure.
In the second case, _ is part of an expression. In this case, the whole expression is turned into a closure, up to any expression delimiter -- by which I mean that if the expression is nested inside another, only the inner expression is turned into a closure. In practice, it is equivalent to f(x => x + 1, 5).
The parser was not sure where to put the beginning of the anonymous function. Sometimes you can solve this by adding another pair of parentheses (though not always):
val m: Map[String, (List[String])=>Loader] =
Map("x" -> (Loader("x", "x1", _:List[String])))
I don’t see any ambiguities here, so it might just not have been smart enough to figure it out. I think, the parser overlooked the possibility to have an anonymous function just after the -> (which also is a library construct and uses implicit magic and all the wicket stuff which makes the little parser’s mind loop).
When you write it as an explicit tuple, it’ll work fine.
val m: Map[String, (List[String])=>Loader] =
Map(("x", Loader("x", "x1", _:List[String])))