Advertisement
Guest User

Untitled

a guest
Jul 28th, 2015
174
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.16 KB | None | 0 0
  1. //
  2. // Validation[E, A] represents either:
  3. // - Success[A]
  4. // - Failure[E]
  5. //
  6. // Isomporphic to scala.Either[E, A] and scalaz.\/[E, A]
  7. //
  8. // Unlike \/[E, A], Validation is not a Monad, but an Applicative
  9. // Functor. So if you want to use it as a Monad you can convert back
  10. // and forth using the `validation` and `disjunction` method
  11. //
  12. // The motivation for a Validation is to provide the instance Applicative
  13. // that accumulate failures through a scalaz.Semigroup[E]
  14. //
  15. // scalaz.NonEmptyList is commonly chosen as a type constructor for
  16. // the type E. As a convenience, an alias scalaz.ValidationNel[E] is
  17. // provided as a shorthand for scalaz.Validation[NonEmptyList[E]], and
  18. // a method Validation#toValidationNel converts Validation[E] to
  19. // ValidationNel[E]
  20. //
  21. //
  22. // \/[A, B] and Validation[A, B] are isomorphic, but Validation
  23. // accumulates errors (lefts) while \/ "fails fast"
  24. //
  25.  
  26. import scalaz._
  27. import scalaz.syntax.validation._
  28.  
  29. scala> val e = "error".failure[Int]
  30. res1: scalaz.Validation[String,Int] = Failure(error)
  31.  
  32. scala> val s = 1.success[String]
  33. res2: scalaz.Validation[String,Int] = Success(1)
  34.  
  35. scala> e.getOrElse(2)
  36. res3: Int = 2
  37.  
  38. scala> e | 2
  39. res4: Int = 2
  40.  
  41. scala> e.fold(_ => 2, _ * 2)
  42. res7: Int = 2
  43.  
  44. scala> e.toOption
  45. res10: Option[Int] = None
  46.  
  47. scala> s.map(_ * 100)
  48. res12: scalaz.Validation[String,Int] = Success(100)
  49.  
  50. scala> val e = Validation.failure[String, Int]("error")
  51. e: scalaz.Validation[String,Int] = Failure(error)
  52.  
  53. scala> val s = Validation.success[String, Int](1)
  54. s: scalaz.Validation[String,Int] = Success(1)
  55.  
  56. //
  57. // Validation is also an Applicative Functor, if the type of
  58. // the error side of the validation is a Semigroup. A number
  59. // of computations are tried. If they are all success, a function
  60. // can combine them into a Success. If any of them fails, the
  61. // individual errors are accumulated.
  62. //
  63. // Here, we combine validation errors using the String Semigroup
  64. //
  65. import scalaz._
  66. import scalaz.syntax.validation._
  67. import scalaz.syntax.apply._
  68. import scalaz.std.string._
  69.  
  70. scala> val e = "error".failure[Int]
  71. res1: scalaz.Validation[String,Int] = Failure(error)
  72.  
  73. scala> val s = 1.success[String]
  74. res2: scalaz.Validation[String,Int] = Success(1)
  75.  
  76. scala> (e |@| e) {_ + _}
  77. res0: scalaz.Unapply[scalaz.Apply,scalaz.Validation[String,Int]]{type M[X] = scalaz.Validation[String,X]; type A = Int}#M[Int] = Failure(errorerror)
  78.  
  79. scala> val es = e.toValidationNel
  80. res4: scalaz.ValidationNel[String,Int] = Failure(NonEmptyList(error))
  81.  
  82. // Use the NonEmptyList semigroup to accumulate errors
  83. scala> (es |@| es){_ + _}
  84. res5: scalaz.Unapply[scalaz.Apply,scalaz.ValidationNel[String,Int]]{type M[X] = scalaz.ValidationNel[String,X]; type A = Int}#M[Int] = Failure(NonEmptyList(error, error))
  85.  
  86. //
  87. // Isomporphism: Validation[E, A] <> \/[E, A]
  88. //
  89.  
  90. import scalaz._
  91. import scalaz.std.either._
  92. import scalaz.syntax.either._
  93. import scalaz.syntax.validation._
  94.  
  95. // \/[E, A] => Validation[E, A]
  96. scala> 1.right[String].validation
  97. res3: scalaz.Validation[String,Int] = Success(1)
  98.  
  99. // Validation[E, A] => \/[E, A]
  100. scala> 1.right[String].validation.disjunction
  101. res4: scalaz.\/[String,Int] = \/-(1)
  102.  
  103. scala> 1.success[String].disjunction
  104. res5: scalaz.\/[String,Int] = \/-(1)
  105.  
  106. // Run a disjunction function and back to validation again
  107. scala> 1.success[String].disjunctioned(identity)
  108. res13: scalaz.Validation[String,Int] = Success(1)
  109.  
  110. scala> 1.success[String] @\/ (identity)
  111. res15: scalaz.Validation[String,Int] = Success(1)
  112.  
  113. //
  114. // Accumulate errrors
  115. //
  116. import scalaz._
  117. import scalaz.syntax.validation._
  118. import scalaz.syntax.apply._
  119. import scalaz.std.AllInstances._
  120.  
  121. scala> ("event 1 ok".success[String] |@| "event 2 failed!".failure[String]) {_ + _}
  122. res0: scalaz.Unapply[scalaz.Apply,scalaz.Validation[String,String]]{type M[X] = scalaz.Validation[String,X]; type A = String}#M[String] = Failure(event 2 failed!)
  123.  
  124. scala> ("event 1 ok".success[String] |@| "event 2 failed!".failure[String] |@| "event 3 failed".failure[String]) {_ + _ + _}
  125. res2: scalaz.Unapply[scalaz.Apply,scalaz.Validation[String,String]]{type M[X] = scalaz.Validation[String,X]; type A = String}#M[String] = Failure(event 2 failed!event 3 failed)
  126.  
  127. scala> ("event 1 ok".success[String] +++ "event 2 failed!".failure[String] +++ "event 3 failed".failure[String])
  128. res4: scalaz.Validation[String,String] = Failure(event 2 failed!event 3
  129.  
  130. //
  131. // ValidationNel[E, A] = Validation[NonEmptyList[E], A]
  132. //
  133.  
  134. import scalaz._
  135. import scalaz.syntax.validation._
  136.  
  137. scala> "error".failureNel
  138. res21: scalaz.ValidationNel[String,Nothing] = Failure(NonEmptyList(error))
  139.  
  140. scala> "ok".successNel
  141. res22: scalaz.ValidationNel[Nothing,String] = Success(ok)
  142.  
  143. //
  144. // Validation => ValidatoinNel
  145. //
  146. scala> "ok".success.toValidationNel
  147. res23: scalaz.ValidationNel[Nothing,String] = Success(ok)
  148.  
  149. scala> "error".failure.toValidationNel
  150. res24: scalaz.ValidationNel[String,Nothing] = Failure(NonEmptyList(error))
  151.  
  152. import scalaz._
  153. import scalaz.syntax.validation._
  154. import scalaz.syntax.apply._
  155. import scalaz.std.AllInstances._
  156.  
  157. scala> ("event 1 ok".successNel[String] +++ "event 2 failed!".failureNel[String] +++ "event 3 failed".failureNel[String])
  158. res1: scalaz.Validation[scalaz.NonEmptyList[String],String] = Failure(NonEmptyList(event 2 failed!, event 3 failed))
  159.  
  160. //
  161. // Option[A] => Validation[E, A]
  162. // toSuccess[E]
  163. // toFailure[B]
  164. //
  165.  
  166. import scalaz._
  167. import syntax.std.option._
  168.  
  169. scala> Some(10).toSuccess("message")
  170. res10: scalaz.Validation[String,Int] = Success(10)
  171.  
  172. scala> None.toSuccess("message")
  173. res11: scalaz.Validation[String,A] = Failure(message)
  174.  
  175. //
  176. // Example (1) of Accumulating errors with Validation
  177. //
  178.  
  179. import scalaz.{ Category => _, _ }
  180. import scalaz.syntax.apply._
  181. import scalaz.syntax.std.option._
  182. import scalaz.syntax.validation._
  183.  
  184. // Data model
  185. case class User(name: String)
  186. case class Category(user: User, parent: Category, name: String, desc: String)
  187.  
  188. // Validation method that Accumulates errors
  189. def nonNull[A](a: A, msg: String): ValidationNel[String, A] = {
  190. Option(a).toSuccess(msg).toValidationNel
  191. }
  192.  
  193. // ValidationNel[String, A] is an applicative functor and we are lifting
  194. // Category into it
  195. def buildCategory(user: User, parent: Category, name: String, desc: String) = (
  196. nonNull(user, "User is mandatory for a normal category") |@|
  197. nonNull(parent, "Parent category is mandatory for a normal category") |@|
  198. nonNull(name, "Name is mandatory for a normal category") |@|
  199. nonNull(desc, "Description is mandatory for a normal category")
  200. )(Category.apply)
  201.  
  202. scala> buildCategory(User("mary"), null, null, "Some category.")
  203. res0: scalaz.Unapply[scalaz.Apply,scalaz.ValidationNel[String,User]]{type M[X] = scalaz.ValidationNel[String,X]; type A = User}#M[Category] = Failure(NonEmptyList(Parent category is mandatory for a normal category, Name is mandatory for a normal category))
  204.  
  205. // Validation method that Fails fast with \/
  206. def nonNull[A](a: A, msg: String): \/[String, A] = {
  207. Option(a) \/> msg
  208. }
  209.  
  210. scala> buildCategory(User("mary"), null, null, "Some category.")
  211. res2: scalaz.Unapply[scalaz.Apply,scalaz.\/[String,User]]{type M[X] = scalaz.\/[String,X]; type A = User}#M[Category] = -\/(Parent category is mandatory for a normal category)
  212.  
  213.  
  214. //
  215. // Example (2) of Accumulating errors with Validation
  216. //
  217. import scalaz._
  218. import scalaz.syntax.validation._
  219. import scalaz.syntax.std.boolean._
  220. import scalaz.std.boolean._
  221. import scalaz.syntax.apply._
  222.  
  223. class Name private(val name: String)
  224. object Name {
  225. def apply(n: String): Validation[String, Name] = {
  226. val valid = n.headOption.exists(_.isUpper)
  227. valid ? (new Name(n)).success[String] | "name must start with a capital letter".failure[Name]
  228. }
  229. }
  230.  
  231. class Age private(val age: Int)
  232. object Age {
  233. def apply(a: Int): Validation[String, Age] = {
  234. (a > 0 && a <= 130) ? (new Age(a)).success[String] | "age must be in range".failure[Age]
  235. }
  236. }
  237.  
  238. class Person private(name: Name, age: Age)
  239. object Person {
  240. def apply(n: String, a: Int): ValidationNel[String, Person] = {
  241. (Name(n).toValidationNel |@| Age(a).toValidationNel) { (n, a) => new Person(n, a) }
  242. }
  243. }
  244. scala> Person("Bob", 31)
  245. res0: scalaz.ValidationNel[String,Person] = Success(Person@2c372ce1)
  246.  
  247. scala> Person("bob", 31)
  248. res1: scalaz.ValidationNel[String,Person] = Failure(NonEmptyList(name must start with a capital letter))
  249.  
  250. //
  251. // Example (3) of Accumulating errors with Validation
  252. //
  253.  
  254. import scalaz._
  255. import scalaz.syntax.validation._
  256. import scalaz.syntax.std.boolean._
  257. import scalaz.std.boolean._
  258. import scalaz.syntax.apply._
  259.  
  260. def empty[A](as: Traversable[A]): Validation[String, Unit] = {
  261. !as.isEmpty ? "expected an empty collection".failure[Unit] | ().success[String]
  262. }
  263.  
  264. def only[A](as: Traversable[A]): Validation[String, A] = {
  265. val firstTwo = as.take(2).toSeq
  266. (firstTwo.size != 1) ? "required exactly on elemenent".failure[A] | firstTwo.head.success[String]
  267. }
  268. //
  269. // combine two validations with the validation applicative
  270. // using only the success value from the first
  271. //
  272. scala> only(Seq(1)).toValidationNel <* empty(Seq.empty).toValidationNel
  273. res4: scalaz.Unapply[scalaz.Apply,scalaz.ValidationNel[String,Int]]{type M[X] = scalaz.ValidationNel[String,X]; type A = Int}#M[Int] = Success(1)
  274.  
  275. //
  276. // Example (3) of Accumulating errors with Validation
  277. //
  278.  
  279. import scalaz._
  280. import scalaz.syntax.validation._
  281. import scalaz.syntax.std.string._
  282. import scalaz.std.string._
  283. import scalaz.syntax.std.boolean._
  284. import scalaz.std.boolean._
  285. import scalaz.syntax.apply._
  286. import scalaz.syntax.traverse._
  287. import scalaz.std.list._
  288.  
  289. // Parse text containing a list of integers, each on a separate line
  290. def parse(text: String): ValidationNel[String, List[Int]] = {
  291. def parseInt(s: String): ValidationNel[String, Int] = {
  292. s.parseInt.leftMap(_.toString).toValidationNel
  293. }
  294. val lines: List[String] = text.lines.toList
  295. val listVals: List[ValidationNel[String, Int]] = lines.map(parseInt)
  296. // Sequence the List using the Validation Applicative Functor
  297. listVals.sequence[({type λ[α]=ValidationNel[String, α]})#λ, Int]
  298. }
  299.  
  300. val badInput =
  301. """42
  302. |aasf
  303. |314
  304. |xxx""".stripMargin
  305. scala> parse(badInput)
  306. res0: scalaz.ValidationNel[String,List[Int]] = Failure(NonEmptyList(java.lang.NumberFormatException: For input string: "aasf", java.lang.NumberFormatException: For input string: "xxx"))
  307.  
  308. val validInput =
  309. """42
  310. |314""".stripMargin
  311. scala> parse(validInput)
  312.  
  313. scala> parse(validInput)
  314. res1: scalaz.ValidationNel[String,List[Int]] = Success(List(42, 314))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement