Michael
Michael

Reputation: 42050

How to compose functions returning Option with orElse?

Suppose I have two functions foo: Int => Option[Int] and bar: Int => Option[Int] and use them to process a list:

val xs: List[Int] = ...
xs flatMap {x => foo(x) orElse bar(x)}

Can I write xs flatMap (foo orElse bar) instead of {x => foo(x) orElse bar(x)} ?
I can use a 3d party library.

Upvotes: 3

Views: 581

Answers (2)

Kolmar
Kolmar

Reputation: 14224

You may use the combine operation (|+|) of the Semigroup typeclass from scalaz.

The default semigroup of Option uses the semigroup of the content and combines the values if both values are present. So to imitate the orElse behaviour, you would need to wrap the Options in the Tags.FirstVal tag.

The documentation for Tags.FirstVal states:

Type tag to choose a scalaz.Semigroup instance that selects the first operand to append.

You can use .subst and .unsubst methods of the Tag, to wrap and unwrap a type T from inside some F[T]. It also seems to me that you'd have to help Scala a bit with type inference.

All in all the combined function looks like this:

type F[T] = Int => Option[T]
val f = Tags.FirstVal.unsubst(
  Tags.FirstVal.subst[F, Int](foo) |+| 
  Tags.FirstVal.subst[F, Int](bar))

And to use it with flatMap, you have to use the implicit conversion from to Option to List in some way. So the flatMap call may look like this:

xs flatMap (f(_))

You can also make the |+| work as orElse if you override the implicit Monoid instance for Option:

import scalaz._, Scalaz._
implicit val inst: Monoid[Option[Int]] = 
  Tags.First.unsubst(scalaz.std.option.optionFirst[Int])

val foo: Int => Option[Int] = x => if (x % 2 == 0) Some(x) else None
val bar: Int => Option[Int] = x => if (x % 3 == 0) Some(x) else None
val xs = List(1, 2, 3, 4, 5, 6, 7, 8)

xs.flatMap((foo |+| bar)(_))

Upvotes: 1

Jesper
Jesper

Reputation: 206776

As far as I know something like this is not available in the standard library, but you can add it yourself using an implicit class:

implicit class WithOrElse[T, R](f: T => Option[R]) {
  def orElse(g: T => Option[R])(x: T) = f(x) orElse g(x)
}

Example:

val foo: Int => Option[Int] = x => if (x % 2 == 0) Some(x) else None
val bar: Int => Option[Int] = x => if (x % 3 == 0) Some(x) else None

val xs = List(1, 2, 3, 4, 5, 6, 7, 8)

xs flatMap (foo orElse bar)

Upvotes: 6

Related Questions