WelcomeTo
WelcomeTo

Reputation: 20591

Use List as monad in Scala

I'm wondering what is idiomatic way to applying some operation on the List if it is not empty, and return empty List (Nil) if list is empty.

  val result= myList match  {
     case Nil => Nil // this one looks bad for me
     case nonEmpty =>  myService.getByFilters(nonEmpty)
  }

Just using map operation on the list will trigger loop, but I want to achieve same result as map for Option type - i.e. do something only once if List is non-empty, and do nothing if List is empty

Upvotes: 1

Views: 258

Answers (4)

elm
elm

Reputation: 20435

By invoking each filter service separately,

myList.flatMap(filter => myService.getByFilters(List(filter)))

it gets an empty list if myList is empty. If performance may be a matter, consider also a parallel version with

myList.par

Upvotes: 0

Odomontois
Odomontois

Reputation: 16338

There is method headOption in List, so you could use option semantic to lift List to Option[List]:

import scala.collection.TraversableLike

implicit class TraversableOption[T <: TraversableLike[_, T]](traversable: T) {
  def opt: Option[T] = traversable.headOption.map(_ => traversable)
}

you can use it as:

val result = myList.opt.fold[List[Int]](Nil)(myService.getByFilters)

Upvotes: 0

yǝsʞǝla
yǝsʞǝla

Reputation: 16422

I think your design is not quite right perhaps. You should be just able to pass any list into the getByFilters function and it should just handle lists of any length. So there should be no need for these sorts of checks.

If the design change is not possible there is nothing wrong with if:

val result = if(myList.isEmpty) Nil else myService.getByFilters(myList)

It's idiomatic because if returns values. Maybe there are other clean ways, I don't know.

If you just want to require non empty list argument you can use HList or alternatively, you can use this trick:

def takesNonEmptyList[T](head: T, tail: T *): List[T] = head :: tail.toList

You can do something fake to make it seem look idiomatic, but I would not recommend it. It's unclear and unnecessary complication:

def getByFilters(xs: List[Int]) = xs.filter(_ % 2 == 0)
val res = l.headOption.map(_ :: l.tail).map(getByFilters).getOrElse(Nil)
println(res)

prints List(2, 4)

Upvotes: 3

ale64bit
ale64bit

Reputation: 6242

If you really want it, you can just implement your own semantic:

  implicit class MySpecialList[T](xs: List[T]) {
    def mapIfNotEmpty[R](f: List[T] ⇒ List[R]): List[R] =
      if (xs.isEmpty) Nil else f(xs)
  }

  def getStuff(xs: List[Int]) = xs.map(_ + " OK")

  val x: List[Int] = List(1,2,3)
  val y: List[Int] = List()

  def main(args: Array[String]): Unit = {
    val xx = x.mapIfNotEmpty(getStuff) // List("1 OK", "2 OK", "3 OK")
    val yy = y.mapIfNotEmpty(getStuff) // List()
  }

Upvotes: 0

Related Questions