-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ior syntax #1540
Ior syntax #1540
Changes from 17 commits
826b211
756d1ed
4610791
50bf4a6
3eb78f8
4350f57
edf7466
2b18941
54fb5c8
831e32f
9e71a77
6924628
f03ab7d
989de6c
0922773
d650b09
12f7e31
b90a962
ef82db6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,6 +75,11 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable { | |
*/ | ||
def toOption: Option[A] = fold(_ => None, Some.apply) | ||
|
||
/** | ||
* Returns Valid values wrapped in Ior.Right, and None for Ior.Left values | ||
*/ | ||
def toIor: Ior[E, A] = fold(Ior.left, Ior.right) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
/** | ||
* Convert this value to a single element List if it is Valid, | ||
* otherwise return an empty List | ||
|
@@ -430,13 +435,18 @@ private[data] trait ValidatedFunctions { | |
} | ||
|
||
/** | ||
* Converts an `Either[A, B]` to an `Validated[A, B]`. | ||
* Converts an `Either[A, B]` to a `Validated[A, B]`. | ||
*/ | ||
def fromEither[A, B](e: Either[A, B]): Validated[A, B] = e.fold(invalid, valid) | ||
|
||
/** | ||
* Converts an `Option[B]` to an `Validated[A, B]`, where the provided `ifNone` values is returned on | ||
* Converts an `Option[B]` to a `Validated[A, B]`, where the provided `ifNone` values is returned on | ||
* the invalid of the `Validated` when the specified `Option` is `None`. | ||
*/ | ||
def fromOption[A, B](o: Option[B], ifNone: => A): Validated[A, B] = o.fold(invalid[A, B](ifNone))(valid) | ||
|
||
/** | ||
* Converts an `Ior[A, B]` to a `Validated[A, B]`. | ||
*/ | ||
def fromIor[A, B](ior: Ior[A, B]): Validated[A, B] = ior.fold(invalid, valid, (_, b) => valid(b)) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
package cats.syntax | ||
|
||
import cats.data.{Ior, IorNel, NonEmptyList} | ||
|
||
trait IorSyntax { | ||
implicit def catsSyntaxIorId[A](a: A): IorIdOps[A] = new IorIdOps(a) | ||
implicit def catsSyntaxListIorNel[A, B](list: List[IorNel[A, B]]): IorNelListOps[A, B] = | ||
new IorNelListOps(list) | ||
} | ||
|
||
final class IorIdOps[A](val a: A) extends AnyVal { | ||
/** | ||
* Wrap a value in `Ior.Right`. | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.Ior | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> "hello".rightIor[String] | ||
* res0: Ior[String, String] = Right(hello) | ||
* }}} | ||
*/ | ||
def rightIor[B]: Ior[B, A] = Ior.right(a) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Context in Either instances uses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's only that way because of the |
||
|
||
/** | ||
* Wrap a value in `Ior.Left`. | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.Ior | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> "error".leftIor[String] | ||
* res0: Ior[String, String] = Left(error) | ||
* }}} | ||
*/ | ||
def leftIor[B]: Ior[A, B] = Ior.left(a) | ||
|
||
/** | ||
* Wrap a value in the right side of `Ior.Both`. | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.Ior | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> "hello".putRightIor("error") | ||
* res0: Ior[String, String] = Both(error,hello) | ||
* }}} | ||
*/ | ||
def putRightIor[B](left: B): Ior[B, A] = Ior.both(left, a) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure about the necessity of these syntax: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kailuowang in this case, I wanted to offer in the syntax the put functions that helped build Both type just for convenience. I really found useful the invalidNel syntax of validated so I wanted to be able to use something similar when I was working with Ior, that is why I added rightIorNel-leftIorNel. This means that you also object to Ior.rightNel-Ior.leftNel functions on the companion object? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you are right, |
||
|
||
/** | ||
* Wrap a value in the left side of `Ior.Both`. | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.Ior | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> "error".putLeftIor("hello") | ||
* res0: Ior[String, String] = Both(error,hello) | ||
* }}} | ||
*/ | ||
def putLeftIor[B](right: B): Ior[A, B] = Ior.both(a, right) | ||
|
||
/** | ||
* Wrap a value in a NonEmptyList in `Ior.Right`. | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.IorNel | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> "hello".rightIorNel[String] | ||
* res0: IorNel[String, String] = Right(hello) | ||
* }}} | ||
*/ | ||
def rightIorNel[B]: IorNel[B, A] = Ior.rightNel(a) | ||
|
||
/** | ||
* Wrap a value in a NonEmptyList in `Ior.Left`. | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.IorNel | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> "error".leftIorNel[String] | ||
* res0: IorNel[String, String] = Left(NonEmptyList(error)) | ||
* }}} | ||
*/ | ||
def leftIorNel[B]: IorNel[A, B] = Ior.leftNel(a) | ||
|
||
/** | ||
* Wrap a value in a NonEmptyList in the right side of `Ior.Both`. | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.IorNel | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> "hello".putRightIorNel[String]("error") | ||
* res0: IorNel[String, String] = Both(NonEmptyList(error),hello) | ||
* }}} | ||
*/ | ||
def putRightIorNel[B](left: B): IorNel[B, A] = Ior.bothNel(left, a) | ||
|
||
/** | ||
* Wrap a value in a NonEmptyList in the left side of `Ior.Both`. | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.IorNel | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> "I got it wrong".putLeftIorNel[String]("hello") | ||
* res0: IorNel[String, String] = Both(NonEmptyList(I got it wrong),hello) | ||
* }}} | ||
*/ | ||
def putLeftIorNel[B](right: B): IorNel[A, B] = Ior.bothNel(a, right) | ||
} | ||
|
||
|
||
final class IorNelListOps[A, B](val list: List[IorNel[A, B]]) extends AnyVal { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that this can be further abstracted. I am also not sure having syntax for such a specific type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kailuowang you mean that those function can be added to the Ior datatype? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just feel this thing is too specific, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I am over thinking, maybe being able to reduce a list of |
||
|
||
/** | ||
* Returns single combined Ior by reducing a list of IorNel | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.Ior | ||
* scala> import cats.data.NonEmptyList | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> List("hello".rightIorNel[String], "error".leftIorNel[String]).reduceToIor | ||
* res0: Ior[NonEmptyList[String], NonEmptyList[String]] = Both(NonEmptyList(error),NonEmptyList(hello)) | ||
* }}} | ||
*/ | ||
def reduceToIor: Ior[NonEmptyList[A], NonEmptyList[B]] = | ||
list.map(_.map(NonEmptyList(_, Nil))).reduce(_ combine _) | ||
|
||
/** | ||
* Returns an Option of a single combined Ior by reducing a list of IorNel | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.Ior | ||
* scala> import cats.data.IorNel | ||
* scala> import cats.data.NonEmptyList | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> List("hello".rightIorNel[String], "error".leftIorNel[String]).reduceToOptionIor | ||
* res0: Option[Ior[NonEmptyList[String], NonEmptyList[String]]] = Some(Both(NonEmptyList(error),NonEmptyList(hello))) | ||
* | ||
* scala> List.empty[IorNel[String, String]].reduceToOptionIor | ||
* res1: Option[Ior[NonEmptyList[String], NonEmptyList[String]]] = None | ||
* }}} | ||
*/ | ||
def reduceToOptionIor: Option[Ior[NonEmptyList[A], NonEmptyList[B]]] = | ||
list.map(_.map(NonEmptyList(_, Nil))) reduceOption (_ combine _) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,71 @@ | ||
package cats | ||
package syntax | ||
|
||
import cats.data.NonEmptyList | ||
import cats.data.Validated.{Invalid, Valid} | ||
import cats.data.{Ior, IorNel, NonEmptyList, ValidatedNel} | ||
|
||
trait ListSyntax { | ||
implicit def catsSyntaxList[A](la: List[A]): ListOps[A] = new ListOps(la) | ||
} | ||
|
||
final class ListOps[A](val la: List[A]) extends AnyVal { | ||
|
||
/** | ||
* Returns an Option of NonEmptyList from a List | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.NonEmptyList | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> val result1: List[Int] = List(1, 2) | ||
* scala> result1.toNel | ||
* res0: Option[NonEmptyList[Int]] = Some(NonEmptyList(1, 2)) | ||
* | ||
* scala> val result2: List[Int] = List.empty[Int] | ||
* scala> result2.toNel | ||
* res1: Option[NonEmptyList[Int]] = None | ||
* }}} | ||
*/ | ||
def toNel: Option[NonEmptyList[A]] = NonEmptyList.fromList(la) | ||
|
||
/** | ||
* Returns a IorNel from a List | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.IorNel | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> val result1: List[String] = List("error 1", "error 2") | ||
* scala> result1.toIorNel(1) | ||
* res0: IorNel[String, Int] = Left(NonEmptyList(error 1, error 2)) | ||
* | ||
* scala> val result2: List[String] = List.empty[String] | ||
* scala> result2.toIorNel(1) | ||
* res1: IorNel[String, Int] = Right(1) | ||
* }}} | ||
*/ | ||
def toIorNel[B](ifEmpty: => B): IorNel[A, B] = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The intention of this isn't very clear. I mean if you read a code There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kailuowang you are right, I didn't make it clear. This is motivated due to a use case I find frequently. If there is some validations made and were returned as a List, meaning that if it is empty everything went well and if it is not then there are errors present, I had to do something like this: errorList match {
case Nil => Valid("this is valid")
case err :: errs => Invalid(NoneEmptyList(err, errs))
} I found convenient to have a syntax that helped me with this for ValidatedNel and IorNel. I do agree with you that if there is not consensus on this I can roll it back and maybe try it again on another pull request when I can make an example illustrating the use of what I proposed. I'll wait for your thoughts on this. |
||
toNel.fold[IorNel[A, B]](Ior.rightNel(ifEmpty))(Ior.left) | ||
|
||
/** | ||
* Returns a ValidatedNel from a List | ||
* | ||
* Example: | ||
* {{{ | ||
* scala> import cats.data.ValidatedNel | ||
* scala> import cats.implicits._ | ||
* | ||
* scala> val result1: List[String] = List("error 1", "error 2") | ||
* scala> result1.toValidatedNel(1) | ||
* res0: ValidatedNel[String, Int] = Invalid(NonEmptyList(error 1, error 2)) | ||
* | ||
* scala> val result2: List[String] = List.empty[String] | ||
* scala> result2.toValidatedNel(1) | ||
* res1: ValidatedNel[String, Int] = Valid(1) | ||
* }}} | ||
*/ | ||
def toValidatedNel[B](ifEmpty: => B): ValidatedNel[A, B] = | ||
toNel.fold[ValidatedNel[A, B]](Valid(ifEmpty))(Invalid(_)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above. |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍