Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic alternative to `if (x) Some(y) else None`

I'm finding the following pattern popping up repeatedly in my code, and my intuition says there must be some idiomatic Scala way to better express this (Monadic or otherwise):

val someCollection: Seq[Thing] = ...
val makeBlah: Seq[Thing] => Blah = ...
...
if (someCollection.nonEmpty) Some(makeBlah(someCollection)) else None

To be more specific, I'm looking for something along the lines of what you can do with Option[T]:

val someOption: Option[Thing] = ...
val makeBlah: Thing => Blah = ...
...
val result: Option[Blah] = someOption.map(makeBlah)

...but with evaluation semantics based on some predicate rather than Some/None pattern matching in map.

While the example above uses a collection--first performing a test on it, optionally followed by an operation--I don't mean to imply a collections specific use case. You could imagine a case where Boolean is lifted or coerced into some monad:

val aThing: Thing = ...
val makeBlah: Thing => Blah = ...
val thingTest: Thing => Boolean ...
// theoretical
implicit def optionOnBoolean(b: Boolean): MonadOps[Option[Boolean]] = ... 
...
// NB: map could either have a Boolean parameter
//     that's always true, or be Unit.
//     Neither seem like good design 
val result: Option[Blah] = thingTest(aThing).map(makeBlah(aThing))

Intuitively this seems like a bad idea to me because it explicitly splits the data flow since you don't really have anything to pass via map.

When looking for a general approach that has "monadic-like" behavior without a closure to capture data, one has to answer the question of what to pass to map and how its connection to the predicate. Here's the type of construct that comes to mind:

val thing: Thing = ....
val makeBlah: Thing => Blah = ...
val thingTest: (Thing) => Boolean = ...
val result: Option[Blah] = WhenOption(thing, thingTest).map(makeBlah)

My question: Does something already exist in Scala proper, or does one have to venture out to Scalaz to get this sort of construct?

Or is there some other approach that is customary/idiomatic Scala?

Edit: My question is close to Scala - "if(true) Some(1)" without having to type "else None" but I wish to address the issue of achieving it without a closure.

like image 851
metasim Avatar asked May 23 '14 13:05

metasim


1 Answers

For completeness:

val someCollection: Seq[Thing] = ...
val makeBlah: Seq[Thing] => Blah = ...

You can use some methods on Option:

Some(someCollection).filterNot(_.isEmpty).map(makeBlah)

or as for comprehension

for(sc <- Some(someCollection) if !someCollection.isEmpty) yield makeBla(sc)

or as pattern match

someCollection match {
  case Seq() => None
  case x => Some(makeBlah(x))
}

But I think the if-then-else approach is the most readable one.

like image 131
ziggystar Avatar answered Nov 02 '22 11:11

ziggystar