I want to write a function
def doSomething(m: Map[X, Y]): Z = ???
for some given types X, Y, Z.
The function will do the same thing for both immutable.Map and mutable.Map.
Is there any way I can write that?
Just declare the argument m to be of type collection.Map[X, Y].
Here is a quick way to find out what the least upper bound of the two types is:
import collection._
def f(a: mutable.Map[Int, Int], b: immutable.Map[Int, Int]) = List(a, b).head
The REPL with tell you that the return type is collection.Map[Int, Int].
Both immutable.Map and mutable.Map extends scala.collection.Map.
package scala
package collection
trait Map[A, +B] extends Iterable[(A, B)] with GenMap[A, B] with MapLike[A, B, Map[A, B]] {
def empty: Map[A, B] = Map.empty
override def seq: Map[A, B] = this
}
looking above you will see this is a generic interface in Scala to describe a Map. therefore your function could be def doSomething(m: scala.collection.Map[X, Y]): Z = ???
you will still have majority of Map interface functions to use, however, those ones that's not shared between mutable and immutable won't be there.
Related
let's say I have:
trait Get[F[_], A, B]{
def get(a:A): F[B]
}
I want to be able to map over the result type B, ie I want to be able to do:
val getFoo: Get[IO, String, Foo] = ???
val foo2Bar: Foo => Bar = ???
val getBar: Get[IO, String, Bar] = getFoo.map(foo2Bar)
I understand that I should implement a Functor instance for Get but I am struggling as I don't know what type signature to use.
I tried the following:
implicit val functor:Functor[Get] = ???
implicit val functor: Functor[Lambda[(F[_], K, A) => Get[F, K, A]]] = ???
but they don't seem to be of the right type as I can't seem to use the functor syntax extension as illustrated at the top. What is the right way of expressing the type here? What would be the equivalent type be if I use the kind-projector plugin?
Try
import cats.syntax.functor._
implicit def functor[F[_]: Functor, A]: Functor[Get[F, A, ?]] = new Functor[Get[F, A, ?]] {
override def map[B, B1](fa: Get[F, A, B])(f: B => B1): Get[F, A, B1] = a => fa.get(a).map(f)
}
Normally breakout would aid in conversion from one collection to another, but it doesn't seem to be able to infer the necessary colleciton constuctor for C:
import scala.collection.breakOut
object Utils {
implicit class IterableExtra[T, C[X] <: Iterable[X]](val list: C[T]) extends AnyVal {
def empty: C[T] = Iterable.empty[T].map(x => x)(breakOut)
}
}
Ideally this would work with minimal reflection, so that it might work in scala.js
Update I was also trying to use this in a different way, and I forgot to have the implicit at the outermost level:
def testIterableEmpty[B, I[X] <: Iterable[X]](implicit cbf: CanBuildFrom[I[B], B, I[B]]): I[B] = {
def emptyIter: I[B] = cbf().result()
emptyIter
}
scala> val x: List[Int] = testIterableEmpty[Int, List]
x: List[Int] = List()
breakOut is defined like so:
def breakOut[From, T, To](implicit b: CanBuildFrom[Nothing, T, To]): CanBuildFrom[From, T, To]
So it cannot be used to avoid passing a CanBuildFrom into your empty method - it requires one itself. Luckily, it is easy to write - you want to create a C[T] out of C[T], and the element type is T, so:
def empty(implicit cbf: CanBuildFrom[C[T], T, C[T]]): C[T] =
Iterable.empty[T].map(x => x)(breakOut)
Tho since you have a CanBuildFrom instance anyway, the implementation using it directly is straightforward too:
def empty(implicit cbf: CanBuildFrom[C[T], T, C[T]]): C[T] =
cbf().result()
I want to add some helpful implicits to both mutable and immutable TreeMaps and TreeSets in Scala.
Here is my attempt:
First try to define the least upper bound of TreeMap and TreeSet that has headOption/lastOption (from GenTraversableLike) and from/to/until (from Sorted):
type SortedCollection[A, Repr <: SortedCollection[A, Repr]] = collection.generic.Sorted[A, Repr] with collection.GenTraversableLike[A, Repr]
Write my util:
implicit class RichSortedCollection[A, Repr <: SortedCollection[A, Repr]](s: SortedCollection[A, Repr]) {
def greaterThanOrEqualTo(a: A): Option[A] = s.from(a).headOption
def lessThan(a: A): Option[A] = s.until(a).lastOption
def lessThanOrEqualTo(a: A): Option[A] = s.to(a).lastOption
}
This only works partially: SortedSet#greaterThan compiles but TreeMap#greaterThan does not. How do I fix it?
TreeMap[A, B] (transitively) extends GenTraversableLike[(A, B), TreeMap[A, B]] and Sorted[A, TreeMap[A, B]], so you could say it's a:
Sorted[A, TreeMap[A, B]] with GenTraversableLike[(A, B), TreeMap[A, B]]
This is close to your type alias, but the first type parameter of Sorted and GenTraverableLike in the type alias SortedCollection must be the same, which they are not above. They simply aren't compatible. That is, Repr = TreeMap[A, B] is fine, but A = (A, B) doesn't make sense.
You're going to have the same issue with all map types, and your only real choice is to re-implement RichSortedCollection for maps as well.
In Scala I want to create a generic new Map class called MyClass. In this class i want to maintain the generality of types and modify only method ++ for Sequence type.
The ++ method must merge the equal object for the same key in this map.
Example
val map1 = ("a"->Seq(1,2))
val map2 = ("a"->Seq(2,3))
the result must be
map1++map2 = ("a"->Seq(1,2,3))
and not
map1++map2 = ("a"->Seq(2,3))
For all other type MyMap must be the same of "classic" Map class.
As others have pointed out in the comments, you should try something first and then come back if you encounter some specific problem. Here's how you can start, given that you want to implement the Map trait:
class MyClass[A, +B] extends Map[A, B] {
def get(key: A): Option[B] = ???
def iterator: Iterator[(A, B)] = ???
def +[B1 >: B](kv: scala.Tuple2[A, B1]): Map[A, B1] = ???
def -(key: A): Map[A, B] = ???
override def ++[B1 >: B](xs: scala.collection.GenTraversableOnce[scala.Tuple2[A, B1]]) = ???
}
You can also start from an existing Map implementation, such as
class MyClass[A, +B] extends scala.collection.immutable.HashMap[A, B] {
override def ++[B1 >: B](xs: scala.collection.GenTraversableOnce[scala.Tuple2[A, B1]]) = ???
}
However, in that case compiler will warn you that inheritance from existing implementation is unwise because of implementation details.
As title of this question suggests: my question is more about form (idiomatic convention) than function. Put Succinctly:
What is the semantic difference between MyCollectionLike and MyCollection?
As examples: What is the difference between StringLike and String or MapLike and Map. Looking closely at the Scala API docs, I can tell that XLike is typically a super-type of X. But, beyond that, I am not clear on the semantic difference between these layers of abstraction.
In practice, If I were creating a new class/trait, understanding this distinction would be helpful when I am choosing names for said class.
My specific problem, where this has come up is as follows:
I want to create the trait: SurjectiveMap[K, T] which can be mixed-in with either Map[K, Set[T]] or MapLike[K, SetLike[T]]. Given that I do not know the semantic difference between *Like and *, I am not sure which to go with.
The same difference as between IFoo and Foo, Bar and BarImpl (except the fact that TraversableLike is super-trait which contains implementation):
The Scala collection library avoids code duplication and achieves the
"same-result-type" principle by using generic builders and traversals
over collections in so-called implementation traits. These traits are
named with a Like suffix; for instance, IndexedSeqLike is the
implementation trait for IndexedSeq, and similarly, TraversableLike is
the implementation trait for Traversable. Collection classes such as
Traversable or IndexedSeq inherit all their concrete method
implementations from these traits.
from Scala collections architecture
I think using the Like traits lets you refine the return (representation) types. This involves a lot more work. Compare:
import collection.generic.CanBuildFrom
object FooMap {
type Coll = FooMap[_, _]
implicit def canBuildFrom[A, B]: CanBuildFrom[Coll, (A, B), FooMap[A, B]] = ???
}
trait FooMap[A, +B] extends Map[A, B] {
def foo = 33
}
def test(f: FooMap[Any, Any]) {
f.map(identity).foo // nope, we ended up with a regular `Map`
}
versus
object FooMap extends collection.generic.ImmutableMapFactory[FooMap] {
override type Coll = FooMap[_, _]
implicit def canBuildFrom[A, B]: CanBuildFrom[Coll, (A, B), FooMap[A, B]] = ???
def empty[A, B]: FooMap[A, B] = ???
}
trait FooMap[A, +B] extends Map[A, B]
with collection.immutable.MapLike[A, B, FooMap[A, B]] {
def foo = 33
override def empty: FooMap[A, B] = FooMap.empty[A, B]
}
def test(f: FooMap[Any, Any]) {
f.map(identity).foo // yes
}
The MapLike trait must be mixed in after the Map trait for the correct return types to kick in.
Still you don't get everything for free it seems, e.g. you will need to override more methods:
override def +[B1 >: B](kv: (A, B1)): FooMap[A, B1] // etc.