Suppose I have a list of int. I can invoke a lift function on it and get another function with type T => Option[T].
val f0: Int => Option[Int] = List(1, 2).lift
println(f0.apply(0)) // Some(1)
println(f0.apply(1)) // Some(2)
println(f0.apply(2)) // None
But how does it work? Why do I can apply a lift (from PartialFunction trait) function to List? Is there some implicit magic?
There is no "implicit magic". List[T] simply is a subclass of PartialFunction[Int, T]
As mentioned List[T] is a subtype of PartialFunction[Int, T]. Now List is not a direct subclass of PartialFunction.
It's the Seq trait that extends PartialFunction in the form trait Seq[+A] extends PartialFunction[Int, A] .
Seq is the trait inherited by collections like List which in turn gives them the methods like Lift etc.
I think its a matter of perspective.
I would look at that as Seqis a PartialFunction that goes from Int values to the element type of the sequence and whose isDefinedAt method returns true for the interval from 0 until length.
Look here.
Likewise for maps all maps extend the trait MapLike which extends PartialFunction. Now a Map[A,B] extends PartialFunction[A,B].
So think of Maps as PartialFunctions where the isDefinedAt method returns true for all defined keys.
I am copying a sample from my worksheet . I assign Map to a PartialFunction
to illustrate the sameness.
val m = Map("a" -> 1, "b" -> 2, "c" -> 3, "d" -> 4)
//> m : scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2, c -> 3
//| , d -> 4)
val f2: PartialFunction[String, Int] = m //> f2 : PartialFunction[String,Int] = Map(a -> 1, b -> 2, c -> 3, d -> 4)
m.isDefinedAt("d") //> res5: Boolean = true
f2.isDefinedAt("e") //> res6: Boolean = false
Look here.
Related
I have this simple program below. I was expecting all results to indicate that a map concatenated with a sequence would give a map. It is strange that the case 3 brings an iterable instead of a map. I guess that this is due to the fact that there are two distinct operators "++" on a map, as shown below. So the question is what is the difference between "Map[_, _]" and "Map[Any, Any]". Thanks for your insights.
def test1[A, B](map: Map[A, B], a: A, B: B) = {
val map2 = map ++ Seq(a -> b)
println("Test 1 " + map.getClass + " / " + map2.getClass)
}
def test2(map: Map[Any, Any], a: Any, B: Any) = {
val map2 = map ++ Seq(a -> b)
println("Test 2 " + map.getClass + " / " + map2.getClass)
}
def test3(map: Map[_, _], a: Any, B: Any) = {
val map2 = map ++ Seq(a -> b)
println("Test 3 " + map.getClass + " / " + map2.getClass)
}
// Test 1 class scala.collection.immutable.Map$Map1 / class scala.collection.immutable.Map$Map2
test1(Map(1 -> "A"), 2, "B")
// Test 2 class scala.collection.immutable.Map$Map1 / class scala.collection.immutable.Map$Map2
test2(Map(1 -> "A"), 2, "B")
// Test 3 class scala.collection.immutable.Map$Map1 / class scala.collection.immutable.$colon$colon
test3(Map(1 -> "A"), 2, "B")
In first 2 cases you have a situation when you have a Map[K, V] and Seq[(K, V)] - because Seq[A] <: Iterable[A], Scala would pick more specific case with ++(seq: Iterable[(K, V)]): Map[K V].
In the third case, however, you are using existential type. It means that "there is some type X and Y for which your type is Map[X, Y]" whereas sequence you are adding is Seq[(A, B)] - A and B might be of type X and Y but since we don't know anything about them we cannot assume that. You can pass empty map of type Map[Nothing, Nothing] or Map[Unit, Unit] = Map(() -> ()) and it would gladly accept it.
What can it do in this case? Well, all Maps are sequences of pairs (that happens to ensure that the first element of the pair is unique) and it provides ++ that does just that - concatenate your Iterable[Z] to that sequence.
So, it all boils down to the fact that _ in type signature means existential type. Map[_, _] is a shortcut for Map[X, Y] forSome { type X; type Y }. You know that there is some type that would fulfill the requirements, but neither you nor the compiler knows enough to prove anything about it. And since Scala (and most typed languages) picks up the most specific definition (what "more specific" means is defined in the languages specification) Map[X, Y] ++ Seq[(X, Y)] picks the version that returns Map[X, Y] and when it cannot prove that types align, it returns Iterable.
If you run this in ammonite to check your output types you'll see that the typ of the third case is Iterable[(Any, Any)] as Scala had to find a common type for (_, _) and (K, V). REPLs shows interred types better than runtime reflection that looses information due to type-erasure.
# def test1[A, B](map: Map[A, B], a: A, b: B) = {
map ++ Seq(a -> b)
}
defined function test1
#
# def test2(map: Map[Any, Any], a: Any, b: Any) = {
map ++ Seq(a -> b)
}
defined function test2
#
# def test3(map: Map[_, _], a: Any, b: Any) = {
map ++ Seq(a -> b)
}
defined function test3
#
# test1(Map(1 -> "A"), 2, "B")
res15: Map[Int, String] = Map(1 -> "A", 2 -> "B")
#
# test2(Map(1 -> "A"), 2, "B")
res16: Map[Any, Any] = Map(1 -> "A", 2 -> "B")
#
# test3(Map(1 -> "A"), 2, "B")
res17: collection.immutable.Iterable[(Any, Any)] = List((1, "A"), (2, "B"))
Can someone maybe explain to me why I get a compiler error below and the best way to do this type of conversion
Thanks
Des
case class A[T](i: Int, x: T)
val set: Set[A[_]] = Set(A(1, 'x'), A(2, 3))
val map: Map[Int, A[_]] = set.map(a => a.i -> a)
type mismatch; found : scala.collection.immutable.Set[(Int, A[_$19]) forSome { type _$19 }] required: Map[Int,A[_]]
There are a couple of things here, first I suppose that A is a case class (otherwise you would need to use the new keyword), second your map returns a Set of tuples (not a Map), third you are returning a A[_] in the map, but a.x returns an Any, not an A:
scala> case class A[T](i: Int, x: T)
defined class A
scala> val set: Set[A[_]] = Set(A(1, 'x'), A(2, 3))
set: Set[A[_]] = Set(A(1,x), A(2,3))
To match the type signature you can use toMap and change Map[Int, A[_]] to Map[Int, _]
scala> val map: Map[Int, _] = set.map(a => a.i -> a.x).toMap
map: Map[Int, _] = Map(1 -> x, 2 -> 3)
If you want to keep the original signature (Map[Int, A[_]]) you need to return an A in the tuple:
scala> val map: Map[Int, A[_]] = set.map(a => a.i -> a).toMap
map: Map[Int,A[_]] = Map(1 -> A(1,x), 2 -> A(2,3))
What would be a functional way to zip two dictionaries in Scala?
map1 = new HashMap("A"->1,"B"->2)
map2 = new HashMap("B"->22,"D"->4) // B is the only common key
zipper(map1,map2) should give something similar to
Seq( ("A",1,0), // no A in second map, so third value is zero
("B",2,22),
("D",0,4)) // no D in first map, so second value is zero
If not functional, any other style is also appreciated
def zipper(map1: Map[String, Int], map2: Map[String, Int]) = {
for(key <- map1.keys ++ map2.keys)
yield (key, map1.getOrElse(key, 0), map2.getOrElse(key, 0))
}
scala> val map1 = scala.collection.immutable.HashMap("A" -> 1, "B" -> 2)
map1: scala.collection.immutable.HashMap[String,Int] = Map(A -> 1, B -> 2)
scala> val map2 = scala.collection.immutable.HashMap("B" -> 22, "D" -> 4)
map2: scala.collection.immutable.HashMap[String,Int] = Map(B -> 22, D -> 4)
scala> :load Zipper.scala
Loading Zipper.scala...
zipper: (map1: Map[String,Int], map2: Map[String,Int])Iterable[(String, Int, Int)]
scala> zipper(map1, map2)
res1: Iterable[(String, Int, Int)] = Set((A,1,0), (B,2,22), (D,0,4))
Note using get is probably preferable to getOrElse in this case. None is used to specify that a value does not exist instead of using 0.
As an alternative to Brian's answer, this can be used to enhance the map class by way of implicit methods:
implicit class MapUtils[K, +V](map: collection.Map[K, V]) {
def zipAllByKey[B >: V, C >: V](that: collection.Map[K, C], thisElem: B, thatElem: C): Iterable[(K, B, C)] =
for (key <- map.keys ++ that.keys)
yield (key, map.getOrElse(key, thisElem), that.getOrElse(key, thatElem))
}
The naming and API are similar to the sequence zipAll.
I know it's possible to pass individual arguments to a vararg function and it's possible to pass a seq using :_* but is it possible to pass both?
for example:
scala> object X { def y(s: String*) = println(s) }
defined module X
scala> X.y("a", "b", "c")
WrappedArray(a, b, c)
scala> X.y(Seq("a", "b", "c"):_*)
List(a, b, c)
scala> X.y("a", Seq("b", "c"):_*)
<console>:9: error: no `: _*' annotation allowed here
(such annotations are only allowed in arguments to *-parameters)
X.y("a", Seq("b", "c"):_*)
^
Edit: In Scala 2.10 (in case that matters)
Hacky but this should work well:
X.y(Seq("a") ++ Seq("b", "c"):_*)
If you look around in the Scala standard library you'll find this sort of pattern in places:
def doIt(arg: Thing)
def doIt(arg1: Thing, arg2: Thing, moreArgs: Thing*)
You can see this, e.g., in Set.+(...). It allows you to have any number of arguments without ambiguity in the overloads.
Addendum
Proof of concept:
scala> class DI { def doIt(i: Int) = 1; def doIt(i1: Int, i2: Int, iMore: Int*) = 2 + iMore.length }
defined class DI
scala> val di1 = new DI
di1: DI = DI#16ac0be1
scala> di1.doIt(0)
res1: Int = 1
scala> di1.doIt(1, 2)
res2: Int = 2
scala> di1.doIt(1, 2, 3)
res3: Int = 3
scala> di1.doIt(1, 2, List(3, 4, 5): _*)
res4: Int = 5
I've been stuck on this for a while now: I have this method
Method(a: (A,B)*): Unit
and I have a map of type Map[A,B], is there a way to convert this map so that I can directly use it as an argument?
Something like:
Method(map.convert)
Thank you.
You can build a sequence of pairs from a Map by using .toSeq.
You can also pass a sequence of type Seq[T] as varargs by "casting" it with : _*.
Chain the conversions to achieve what you want:
scala> val m = Map('a' -> 1, 'b' -> 2, 'z' -> 26)
m: scala.collection.immutable.Map[Char,Int] = Map(a -> 1, b -> 2, z -> 26)
scala> def foo[A,B](pairs : (A,B)*) = pairs.foreach(println)
foo: [A, B](pairs: (A, B)*)Unit
scala> foo(m.toSeq : _*)
(a,1)
(b,2)
(z,26)
I'm not sure but you can try converting map into array of tuples and then cast it to vararg type, like so:
Method(map.toArray: _*)
Just convert the map to a sequence of pairs using ".toSeq" and pass it to the var-args method postfixed with ":_*" (which is Scala syntax to allow passing a sequence as arguments to a var-args method).
Example:
def m(a: (String, Int)*) { for ((k, v) <- a) println(k+"->"+v) }
val x= Map("a" -> 1, "b" -> 2)
m(x.toSeq:_*)
In repl:
scala> def m(a: (String, Int)*) { for ((k, v) <- a) println(k+"->"+v) }
m: (a: (String, Int)*)Unit
scala> val x= Map("a" -> 1, "b" -> 2)
x: scala.collection.immutable.Map[java.lang.String,Int] = Map(a -> 1, b -> 2)
scala> m(x.toSeq:_*)
a->1
b->2