I'm learning Scala and already had to find out that the concept of monads is a bit too sophisticated for my current level of knowledge. However, my goal is at least to make a very simple class that can be used with the for
expression and additionally a filter.
From my point of understanding, the following rules apply:
map
.withFilter
.My minimal classes look like this:
class Grid(private val fields: IndexedSeq[Field])
class Field(val name: String, val isVisible: Boolean)
What I want to achieve is be able to do the following:
for(f <- grid) yield f.name // needs map
for(f <- grid; if f.isVisisble) yield f.name // needs map + withFilter
However, I have a hard time finding examples with that kind of simplicity. It's ok if the solution is "tailored" to the two classes shown above instead of being a general solution that can be applied to any classes. Unterstanding the implementation for this simple example would definitely help me. Any help is appreciated, thank you.
Edit:
As Lee pointed out, my intention only seem to work for generic types. I assume it would make more sense if I forget about the class Field
and redefine Grid
as follows:
class Grid[E](private val fields: IndexedSeq[E])
In this case you can just pass the map
call on to the wrapped collection fields
.
For withFilter
you can call the filter
method on fields
, but I think that's not entirely in line with the semantics that withFilter
is supposed to have.
case class Grid[E](private val fields: IndexedSeq[E]) {
def map[R](f: E => R): Grid[R] = new Grid(fields map f)
def withFilter(p: E => Boolean): Grid[E] = new Grid(fields filter p)
}
A more correct, but convoluted implementation of what you are asking would be:
case class Grid[E](private val fields: IndexedSeq[E]) {
def map[R](f: E => R): Grid[R] = new Grid(fields map f)
def withFilter(p: E => Boolean): WithFilter = new WithFilter(p)
class WithFilter(p: E => Boolean) {
def map[R](f: E => R): Grid[R] = new Grid(fields.withFilter(p).map(f))
def withFilter(q: E => Boolean): WithFilter = new WithFilter(x => p(x) && q(x))
}
}
That way, withFilter
will work lazily as expected.
It will work with even not generic definition of Grid
, yet map
isn't what you expect:
case class Field(name: String, isVisible: Boolean)
case class Grid(val fields: IndexedSeq[Field]) {
def map[B](f: Field => B): IndexedSeq[B] =
fields.map(f)
def filter(f: Field => Boolean): Grid =
new Grid(fields.filter(f))
}
val grid = new Grid(Vector(Field("foo", true), Field("bar", false)))
// works
for { f <- grid } yield f.name
// res7: IndexedSeq[String] = Vector(foo, bar)
for { f <- grid; if f.isVisible } yield f.name
// res13: IndexedSeq[String] = Vector(foo)
For-comprehension de-sugaring is syntax-based. It rewrites the expression using .map
.flatMap
.filter
etc and then type-checks (AFAIK).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With