The case class as a wrapper class for the collection. What about the map / foldLeft /

What I'm trying to do is create a case class that I can use when matching patterns that has exactly one field, for example. immutable set. In addition, I would like to use functions such as map, foldLeft, etc., which should be passed to the set. I tried this, as in the following:

case class foo(s:Set[String]) extends Iterable[String] { override def iterator = s.iterator } 

Now, if I try to use, for example, the display function, I get an error like:

 var bar = foo(Set() + "test1" + "test2") bar = bar.map(x => x) found : Iterable[String] required: foo bar = bar.map(x => x) ^ 

The type error is absolutely beautiful (in my understanding). However, I am wondering how to implement a case-wrapper class for a collection so that you can call map, foldLeft, etc. And still get an object of class case. Do I need to override all these functions, or is there some other way?

Edit

I tend to make a RΓ©gis Jean-Gilles decision that works for me. However, after Googling for hours, I found another interesting Scala feature called SetProxy . I could not find trivial examples, so I'm not sure if this trait does what I want:

  • a custom type appears, i.e. other type than Set
  • the type must be a case class (we want to perform pattern matching)
  • we need to "delegate" the map, foldLeft, etc. methods, which should pass a call to our actual set and return the resulting set wrapped arround in a new type

My first idea was to extend Set , but my custom type Foo already extends another class. Therefore, the second idea was to mix Iterable and IterableLike attributes. Now I am blushing about the SetProxy , which made me think about what is the β€œbest” way. What are your thoughts and feelings?

Since I started learning Scala three days ago, any pointers have been much appreciated!

+4
source share
2 answers

Hmm, that sounds to me, but Scala says that the variable b is of type Iterable [String], and not of type Foo, that is, I don’t see how IterableLike helps in this situation.

You're right. A simple inheritance from IterableLike , as shown by mpartel, will make the return type of some methods more accurate (for example, the filter that Foo will return), but for others like map flatMap you will need to specify appopriate CanBuildFrom implicitly. Here is a piece of code that does just that:

 import collection.IterableLike import collection.generic.CanBuildFrom import collection.mutable.Builder case class Foo( s:Set[String] ) extends Iterable[String] with IterableLike[String, Foo] { override def iterator = s.iterator override protected[this] def newBuilder: scala.collection.mutable.Builder[String, Foo] = new Foo.FooBuilder def +(elem: String ): Foo = new Foo( s + elem ) } object Foo { val empty: Foo = Foo( Set.empty[String] ) def apply( elems: String* ) = new Foo( elems.toSet ) class FooBuilder extends Builder[String, Foo] { protected var elems: Foo = empty def +=(x: String): this.type = { elems = elems + x; this } def clear() { elems = empty } def result: Foo = elems } implicit def canBuildFrom[T]: CanBuildFrom[Foo, String, Foo] = new CanBuildFrom[Foo, String, Foo] { def apply(from: Foo) = apply() def apply() = new FooBuilder } } 

And some tests in repl:

 scala> var bar = Foo(Set() + "test1" + "test2") bar: Foo = (test1, test2) scala> bar = bar.map(x => x) // compiles just fine because map now returns Foo bar: Foo = (test1, test2) 
+5
source

Inheriting IterableLike[String, Foo] gives you all those methods that return Foo. IterableLike requires you to run newBuilder in addition to iterator .

 import scala.collection.IterableLike import scala.collection.mutable.{Builder, SetBuilder} case class Foo(stuff: Set[String]) extends Iterable[String] with IterableLike[String, Foo] { def iterator: Iterator[String] = stuff.iterator protected[this] override def newBuilder: Builder[String, Foo] = { new SetBuilder[String, Set[String]](Set.empty).mapResult(Foo(_)) } } // Test: val a = Foo(Set("a", "b", "c")) val b = a.map(_.toUpperCase) println(b.toList.sorted.mkString(", ")) // Prints A, B, C 
+1
source

Source: https://habr.com/ru/post/1480318/


All Articles