Skip to content

Instantly share code, notes, and snippets.

@satyagraha
Last active December 17, 2016 23:58
Show Gist options
  • Save satyagraha/897e427bfb5ed203e9d3054ac6705704 to your computer and use it in GitHub Desktop.
Save satyagraha/897e427bfb5ed203e9d3054ac6705704 to your computer and use it in GitHub Desktop.
package my.cats
import cats.Semigroup
import cats.data.Validated
import cats.syntax.CartesianBuilder
import cats.syntax.cartesian._
import scala.util.control.NonFatal
case class Message(text: String)
object Message extends {
implicit object MessageSemigroup extends Semigroup[Message] {
override def combine(x: Message, y: Message) =
Message(x.text + " | " + y.text)
}
implicit def toMessage(throwable: Throwable): Message =
Message(throwable.getMessage)
}
case class Name(nameRepr: String)
case class Date(dateRepr: String)
// the key issue is that this constructor may throw an exception during initial checks
case class User(name: Name, date: Date) {
require(name.nameRepr == "joe", s"only joe allowed as name")
}
object CheckCats {
import Validated._
def tryNonFatal[E, A](f: => A)(implicit mapper: Throwable => E): Validated[E, A] =
try
valid(f)
catch {
case NonFatal(t) => invalid(mapper(t))
}
def tryNonFatalMessage[A](f: => A): Validated[Message, A] =
tryNonFatal(f)
def validateName(nameRepr: String): Validated[Message, Name] = {
if (nameRepr.isEmpty)
invalid(Message("name cannot be blank"))
else
valid(Name(nameRepr))
}
def validateDate(dateRepr: String): Validated[Message, Date] = {
if (dateRepr.isEmpty)
invalid(Message("date cannot be blank"))
else
tryNonFatal {
if (dateRepr != "now")
throw new IllegalArgumentException("only now allowed")
else
Date(dateRepr)
}
}
def main(args: Array[String]): Unit = {
val nameRepr = "joe"
val dateDepr = "now"
// the type of valids is: CartesianBuilder[Validated[Message, _]]#CartesianBuilder2[Name, Date]
val valids = validateName(nameRepr) |@| validateDate(dateDepr)
// the result type here is Validated[Message, Validated[Message, Date]] - want to merge these
val res0 = tryNonFatalMessage {
valids.map(User.apply)
}
println(s"res0: $res0")
// the result type here is Validated[Message, Date] - a bit clumsy
val res1 = res0.fold(m => Validated.invalid[Message, User](m), identity)
println(s"res1: $res1")
// the result type here is Validated[Message, Date] - tupling is dubious
val res2 = valids.tupled andThen (args => tryNonFatalMessage((User.apply _).tupled(args)))
println(s"res2: $res2")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment