Skip to content

Instantly share code, notes, and snippets.

@kolemannix
Created August 29, 2017 21:50
Show Gist options
  • Save kolemannix/a483024d6bfa72b60884315d9db6892c to your computer and use it in GitHub Desktop.
Save kolemannix/a483024d6bfa72b60884315d9db6892c to your computer and use it in GitHub Desktop.
NonEmpty String trim example (Scala)
object Sanitizing {
case class NonEmptyString(s: String) {
require(s.nonEmpty)
}
type EmptiableStringMagnet = Either[String, Option[String]]
}
trait Sanitizing {
import Sanitizing._
implicit def stringEmptiableStringWitness(s: String): EmptiableStringMagnet = Left(s)
implicit def optStringEmptiableStringWitness(s: Option[String]): EmptiableStringMagnet = Right(s)
private def superTrimImpl(s: String): Option[NonEmptyString] = s.trim match {
case trimmed if trimmed.isEmpty => None
case trimmed if trimmed.nonEmpty => Some(NonEmptyString(trimmed))
}
def superTrim(s: EmptiableStringMagnet): Option[NonEmptyString] = s match {
case Left(s) => superTrimImpl(s)
case Right(maybeS: Option[String]) => maybeS.flatMap(superTrimImpl(_))
}
/* Only needed if you want extension methods... "foo".supahTrim */
implicit class StringOps(s: String) {
def supahTrim: Option[NonEmptyString] = superTrim(s)
}
implicit class OptionStringOps(s: Option[String]) {
def supahTrim: Option[NonEmptyString] = superTrim(s)
}
}
// Usage
object Example extends Sanitizing {
val foo = " foo "
val bar = Some(" ")
val fooTrimmed: Option[Sanitizing.NonEmptyString] = superTrim(foo)
val barTrimmed: Option[Sanitizing.NonEmptyString] = superTrim(bar)
println(fooTrimmed) // > Some(NonEmptyString("foo"))
println(barTrimmed) // > None
foo.supahTrim == superTrim(foo) // > true
}
@kolemannix
Copy link
Author

The context of this gist was a discussion about a trim method that returns an Option, populated if the trimmed value still has content, but that can also accept either a String or Option[String]. Method overloading is the obvious solution, but this is more fun and would prove more scalable to large use cases (Magnet pattern vs Overloading is very well explored here: http://spray.io/blog/2012-12-13-the-magnet-pattern/)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment