简体   繁体   中英

Fold on Option vs Fold on Collection

In Scala, to express this:

val opt:Option[String] = Some("a")
if(opt.isEmpty) "" else opt.get

you can use this fold pattern on Options :

opt.fold("")(_)

However fold on collections behaves differently. How can I achieve the same for collections, without having to write:

case class Container(description: String, collection: Seq)
val col = Seq()
if(col.nonEmpty) {
    Some(Container("some description", col))
} else {
    None
}

In other words, how can I elegantly return Some() collection only when it is not empty?

Note, that your examples for Option and Seq aren't equivalent: in the former case, the result is the value that was inside the Option or an empty string, while in the latter case, you end up returning the collection itself , not its contents.

You can do an equivalent of what you did with Option with a fold on collections too, depending on how you want to deal with the multiple values in the collection. For example:

seqOfStrings.foldLeft("") { _ + _ }

would concatenate all of the strings in the collection into one long string (you could get the same result with seqOfStrings.mkString ). Note, that this uses foldLeft rather than fold , because the former is (supposed to be) parallelizable, and is not guaranteed to process the results in order. Here is an example with fold :

 seqOfInts.fold(0) { _ + _ }

This is, of course, the same as seqOfInts.sum

The idea of fold (left/right) is combining ("folding") all the elements of a collection into a single value. Option is a special case, because it can only ever contain a single element, so the transformer function does not need to take two arguments, and looks a lot like the (opposite of) getOrElse - thus your confusion.

What you are trying to do with the collection is not really a use case for fold. You could use headOption for that as other answers suggested, or, better yet, pattern match:

  col match {
     case Seq() => None
     case x => Some(Container("some description", x))
  }

You could wrap it into the companion object, to look prettier:

  object Container {
     def apply[T](seq: Seq[T]) = seq match {
        case Seq() => None
        case s => Container("some description", x))
     }
  }

Now, you can just do Container(seq) elsewhere.

Your expectation from fold is wrong, it is not about Option s it is about working with collection and it works with Option because they are also collections.

With fold you are providing a start value with first parameter and providing a function which will be applied to collection.

About your question: Code you shared looks fine, but if you really want to map some option you can use headOption and map it to value you want but this won't make your code more elegant or clear

This is a bit shorter, although also not really elegant:

Option(col.nonEmpty).collect { case true => Container("some description", col) }

Or, but IMO even uglier (I just saw that Hüseyin suggested it as well):

col.headOption.map(Container("some description", col))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM