- http://www.scalautils.org/user_guide/OrAndEvery
- https://github.com/scalatest/scalatest/blob/release-2.0.RC2-for-scala-2.10/src/main/scala/org/scalautils/Or.scala
- https://github.com/scalatest/scalatest/blob/release-2.0.RC2-for-scala-2.10/src/main/scala/org/scalautils/Every.scala
- https://github.com/scalaz/scalaz/blob/v7.0.4/core/src/main/scala/scalaz/Validation.scala
Last active
December 26, 2015 04:59
-
-
Save xuwei-k/7097510 to your computer and use it in GitHub Desktop.
ScalaUtils vs Scalaz7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.0.4" | |
scalaVersion := "2.10.3" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scalaz._, Scalaz._ | |
object Main extends App{ | |
type ErrorMessage = String | |
def parseName(input: String): ValidationNel[ErrorMessage, String] = { | |
val trimmed = input.trim | |
if (!trimmed.isEmpty) Success(trimmed) else s""""${input}" is not a valid name""".failNel | |
} | |
def parseAge(input: String): ValidationNel[ErrorMessage, Int] = { | |
try { | |
val age = input.trim.toInt | |
if (age >= 0) Success(age) else s""""${age}" is not a valid age""".failNel | |
} | |
catch { | |
case _: NumberFormatException => s""""${input}" is not a valid integer""".failNel | |
} | |
} | |
case class Person(name: String, age: Int) | |
def parsePerson(inputName: String, inputAge: String): ValidationNel[ErrorMessage, Person] = { | |
val name = parseName(inputName) | |
val age = parseAge(inputAge) | |
(name |@| age) { Person(_, _) } | |
} | |
implicit val personEqual: Equal[Person] = Equal.equalA[Person] | |
implicit val personShow: Show[Person] = Show.showA[Person] | |
parsePerson("Bridget Jones", "29") assert_=== Success(Person("Bridget Jones", 29)) | |
parsePerson("Bridget Jones", "") assert_=== """"" is not a valid integer""".failNel | |
parsePerson("Bridget Jones", "-29") assert_=== """"-29" is not a valid age""".failNel | |
parsePerson("", "") assert_=== Failure(NonEmptyList(""""" is not a valid name""", """"" is not a valid integer""")) | |
// List(parseAge("29"), parseAge("30"), parseAge("31")).combined | |
List(parseAge("29"), parseAge("30"), parseAge("31")).sequenceU assert_=== Success(List(29, 30, 31)) | |
// List(parseAge("29"), parseAge("-30"), parseAge("31")).combined | |
List(parseAge("29"), parseAge("-30"), parseAge("31")).sequenceU assert_=== """"-30" is not a valid age""".failNel | |
// List(parseAge("29"), parseAge("-30"), parseAge("-31")).combined | |
List(parseAge("29"), parseAge("-30"), parseAge("-31")).sequenceU assert_=== Failure(NonEmptyList(""""-30" is not a valid age""", """"-31" is not a valid age""")) | |
// List("29", "30", "31").validatedBy(parseAge) | |
List("29", "30", "31").traverseU(parseAge) assert_=== Success(List(29, 30, 31)) | |
// List("29", "-30", "31").validatedBy(parseAge) | |
List("29", "-30", "31").traverseU(parseAge) assert_=== """"-30" is not a valid age""".failNel | |
// List("29", "-30", "-31").validatedBy(parseAge) | |
List("29", "-30", "-31").traverseU(parseAge) assert_=== Failure(NonEmptyList(""""-30" is not a valid age""", """"-31" is not a valid age""")) | |
// parseName("Dude") zip parseAge("21") | |
parseName("Dude") tuple parseAge("21") assert_=== Success(("Dude", 21)) | |
// parseName("Dude") zip parseAge("-21") | |
parseName("Dude") tuple parseAge("-21") assert_=== """"-21" is not a valid age""".failNel | |
// parseName("") zip parseAge("-21") | |
parseName("") tuple parseAge("-21") assert_=== Failure(NonEmptyList(""""" is not a valid name""", """"-21" is not a valid age""")) | |
def isRound(i: Int): ValidationNel[ErrorMessage, Int] = | |
if (i % 10 == 0) Success(i) else (i + " was not a round number").failNel | |
def isDivBy3(i: Int): ValidationNel[ErrorMessage, Int] = | |
if (i % 3 == 0) Success(i) else (i + " was not divisible by 3").failNel | |
type ValidationNelErr[+A] = ValidationNel[ErrorMessage, A] | |
val _isRound = Kleisli[ValidationNelErr, Int, Int](isRound) | |
val _isDievBy3 = Kleisli[ValidationNelErr, Int, Int](isDivBy3) | |
val f = (_isRound *> _isDievBy3).run _ | |
parseAge("30") flatMap f assert_=== Success(30) | |
parseAge("33") flatMap f assert_=== "33 was not a round number".failNel | |
parseAge("20") flatMap f assert_=== "20 was not divisible by 3".failNel | |
parseAge("31") flatMap f assert_=== Failure(NonEmptyList("31 was not a round number", "31 was not divisible by 3")) | |
} | |
You can use traverseU for validatedBy
You're right! I just updated. thanks :)
I'm wondering what would be the best way to have both power of Scalaz (things compose easily) and the naming decisions taken in ScalaUtils - that is to have among others:
- "Int Or ErrorMessage" (where the positive case comes first) instead of "Validation[ErrorMessage, Int]"
- "Bad(error)" instead of "error.failNel"
A bunch of conversion functions between the corresponding concepts? A bunch of typealiases? Both?
I would hope you could just define Scalaz type class instances for Or and Every if you wanted to use both libraries together. What specifically would you want to do? I.e., how you would want to compose things with Scalaz? Once you have some specific goals we could try and figure out what typeclasses would be needed for that, then just define them. It would be interesting to see if that would work.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice comparison. I wasn't aware Scalaz had the tuple method, so that's nice to learn. You can use traverseU for validatedBy. I'm not sure how to update a gist with git, so here's what that would look like:
// List("29", "30", "31").validatedBy(parseAge)
List("29", "30", "31").traverseU(parseAge) assert_=== Success(List(29, 30, 31))
// List("29", "-30", "31").validatedBy(parseAge)
List("29", "-30", "31").traverseU(parseAge) assert_=== """"-30" is not a valid age""".failNel
// List("29", "-30", "-31").validatedBy(parseAge)
List("29", "-30", "-31").traverseU(parseAge) assert_=== Failure(NonEmptyList(""""-30" is not a valid age""", """"-31" is not a valid age"""))