Skip to content

Instantly share code, notes, and snippets.

@makingthematrix
Last active March 9, 2021 17:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save makingthematrix/98b2c4e168d2e77648f0bcbc74523e6d to your computer and use it in GitHub Desktop.
Save makingthematrix/98b2c4e168d2e77648f0bcbc74523e6d to your computer and use it in GitHub Desktop.
Many happy early returns
final case class Foo(str: String)
final case class Bar(n: Int)
val seq: Seq[Foo] = Seq(Foo("1"), Foo("-3"), Foo("42"), Foo("5"), Foo("0"))
def complexConversion(foo: Foo): Bar = Bar(foo.str.toInt)
def complexValidation(bar: Bar): Boolean = bar.n == 42
// 1. the Java-like version
def findFirstValidBar1(seq: Seq[Foo]): Option[Bar] = {
for (foo <- seq) {
val bar = complexConversion(foo)
if (complexValidation(bar)) return Some(bar)
}
None
}
findFirstValidBar1(seq)
// 2. find + map
def findFirstValidBar2(seq: Seq[Foo]): Option[Bar] =
seq.find(foo => complexValidation(complexConversion(foo)))
.map(complexConversion)
findFirstValidBar2(seq)
// 3. collectFirst
def findFirstValidBar3(seq: Seq[Foo]): Option[Bar] =
seq.collectFirst {
case foo if complexValidation(complexConversion(foo)) => complexConversion(foo)
}
findFirstValidBar3(seq)
// 4. unapply + collectFirst
object ValidBar4 {
def unapply(foo: Foo): Option[Bar] = {
val bar = complexConversion(foo)
if (complexValidation(bar)) Some(bar) else None
}
}
def findFirstValidBar4(seq: Seq[Foo]): Option[Bar] =
seq.collectFirst {
case ValidBar4(bar) => bar
}
findFirstValidBar4(seq)
def safeComplexConversion(foo: Foo): Option[Bar] = foo.str.toIntOption.map(Bar(_))
// if you use don't use Scala 2.13 you need Try(foo.str.toInt).toOption
val unsafeSeq: Seq[Foo] = Seq(Foo("1"), Foo("boom!"), Foo("42"), Foo("5"), Foo("0"))
// 5. Safe Java-like
def findFirstValidBar5(seq: Seq[Foo]): Option[Bar] = {
for (foo <- seq)
safeComplexConversion(foo) match {
case Some(bar) if complexValidation(bar) => return Some(bar)
case _ =>
}
None
}
findFirstValidBar5(unsafeSeq)
// 6. Safe unapply + collectFirst
object ValidBar6 {
def unapply(foo: Foo): Option[Bar] =
safeComplexConversion(foo).find(complexValidation)
}
def findFirstValidBar6(seq: Seq[Foo]): Option[Bar] =
seq.collectFirst {
case ValidBar6(bar) => bar
}
findFirstValidBar6(unsafeSeq)
// 7. Extracted trait
// one such trait for the whole codebase
trait Deconstruct[From, To] {
def convert(from: From): Option[To]
def validate(to: To): Boolean
def unapply(from: From): Option[To] = convert(from).find(validate)
}
// one for each implementation of convert and validate
object ValidBar7 extends Deconstruct[Foo, Bar] {
override def convert(foo: Foo): Option[Bar] = foo.str.toIntOption.map(Bar(_))
override def validate(bar: Bar): Boolean = bar.n == 42
}
// for each use
def findFirstValidBar7(seq: Seq[Foo]): Option[Bar] =
seq.collectFirst {
case ValidBar7(bar) => bar
}
findFirstValidBar7(unsafeSeq)
// 8. a lazy iterator
def findFirstValidBar8(seq: Seq[Foo]): Option[Bar] =
seq.iterator
.map(safeComplexConversion)
.find(_.exists(complexValidation))
.flatten
findFirstValidBar8(unsafeSeq)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment