-
-
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
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1540 +/- ##
=========================================
+ Coverage 92.48% 92.5% +0.02%
=========================================
Files 246 247 +1
Lines 3885 3896 +11
Branches 133 129 -4
=========================================
+ Hits 3593 3604 +11
Misses 292 292
Continue to review full report at Codecov.
|
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.
In general I like the idea. I don't see any glaringly obvious problems.
@@ -187,6 +187,18 @@ class ValidatedTests extends CatsSuite { | |||
} | |||
} | |||
|
|||
test("fromIor consistent with Ior.toValidated"){ | |||
forAll { (i: Ior[String, Int], s: String) => |
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.
What is s used for here?
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.
@ChristopherDavenport That just slipped, I will remove it. Thanks!
* 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Context in Either instances uses asRight
so perhaps asRightIor
. Depends on what sort of syntax maintainers would like to see but asRightIor
makes more sense to me.
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.
It's only that way because of the Either.right
projection ambiguity. The lack of such functions on Ior
suggests to me that this way would be nicest.
@@ -3,6 +3,7 @@ package cats | |||
package object data { | |||
type NonEmptyStream[A] = OneAnd[Stream, A] | |||
type ValidatedNel[+E, +A] = Validated[NonEmptyList[E], A] | |||
type IorNel[+B, +A] = Ior[NonEmptyList[B], NonEmptyList[A]] |
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.
I am not sure about this definition now. Maybe it should be similar to ValidatedNel
type IorNel[+B, +A] = Ior[NonEmptyList[B], A]
I decided to define it with a NonEmptyList on the right also because I saw some utility on the combine case (or use of Ior's append function to be exact which can be accessed through semigroup syntax function |+|). But when comparing the combine for Either and Validated I think I got it wrong. Ior's flatmap does accumulate errors only if the case is Ior.Both, so it seems appropiate to leave it similar to ValidatedNel's type definition. I would love some feedback on this.
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.
Personally I cannot see a use-case for IorNel
as it was before; did you have one in mind? That might help to decide.
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.
@edmundnoble I am going to change it to type IorNel[+B, +A] = Ior[NonEmptyList[B], A]
to keep consistency with ValidatedNel. I do have a use case for my original proposal but that is when you get to combine Iors with the |+| operator. My main argument to change it is that in the flatMap you get tu accumulate errors on the left side of Both
so it is the natural usage of it.
@@ -47,6 +49,7 @@ sealed abstract class Ior[+A, +B] extends Product with Serializable { | |||
final def unwrap: Either[Either[A, B], (A, B)] = fold(a => Left(Left(a)), b => Left(Right(b)), (a, b) => Right((a, b))) | |||
|
|||
final def toEither: Either[A, B] = fold(Left(_), Right(_), (_, b) => Right(b)) | |||
final def toValidated: Validated[A, B] = fold(Invalid(_), Valid(_), (_, b) => Valid(b)) |
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.
👍
@@ -76,6 +76,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 comment
The reason will be displayed to describe this comment to others. Learn more.
👍
* 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 comment
The reason will be displayed to describe this comment to others. Learn more.
It's only that way because of the Either.right
projection ambiguity. The lack of such functions on Ior
suggests to me that this way would be nicest.
@@ -3,6 +3,7 @@ package cats | |||
package object data { | |||
type NonEmptyStream[A] = OneAnd[Stream, A] | |||
type ValidatedNel[+E, +A] = Validated[NonEmptyList[E], A] | |||
type IorNel[+B, +A] = Ior[NonEmptyList[B], NonEmptyList[A]] |
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.
Personally I cannot see a use-case for IorNel
as it was before; did you have one in mind? That might help to decide.
* 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 comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure about the necessity of these syntax:
putRightIor
putLeftIor
rightIorNel
leftIorNel
putRightIorNel
putLeftIorNel
I am just not sure if the convenience of these methods overrides the cost of having them on everything.
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.
@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 comment
The reason will be displayed to describe this comment to others. Learn more.
I think you are right, rightIorNel
leftIorNel
look fine to me now.
} | ||
|
||
|
||
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 comment
The 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
I just feel this thing is too specific,
F[G[A, B]] => G[H[A], H[B]]
. if F
is a Functor
and Reducable
I feel like that want to see this function on a type class or companion obect
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.
Maybe I am over thinking, maybe being able to reduce a list of Ior
s conveniently has good value. Generally speaking, I feel a good part of your PR can get easy consensus and this is a place I am less sure.
* 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 comment
The 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 result1.toIorNel(1)
, it's not obvious that the result1
is the error list while 1
is the valid result. Users will have to read the code here to understand the intention. I am not sure about such syntax addition.
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.
@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.
* }}} | ||
*/ | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
thanks very much @leandrob13, I like most of the PR, there are some syntax that I am not sure about. But I am more a minimalist when it comes to API design. So if other people see values in those syntax that I am not sure about I can be persuaded. Or if you want to separate those to another PR I'll gladly give my thumbs up on this one. |
@kailuowang I agree with you, my specific comments are above. Waiting for your feedback to decide what to do next. |
… list syntax object
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.
Thanks very much! 👍
} | ||
|
||
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
I just feel this thing is too specific,
F[G[A, B]] => G[H[A], H[B]]
. if F
is a Functor
and Reducable
I feel like that want to see this function on a type class or companion obect
} | ||
|
||
|
||
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 comment
The 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 Ior
s conveniently has good value. Generally speaking, I feel a good part of your PR can get easy consensus and this is a place I am less sure.
* 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 comment
The reason will be displayed to describe this comment to others. Learn more.
I think you are right, rightIorNel
leftIorNel
look fine to me now.
@kailuowang Thanks! I will follow your advice and prepare another PR to illustrate better the proposals I made. I was actually thinking of a reduceable for the list of iors reduce proposal and thanks to your hint I might work on that too. |
Merging with two 👍 |
Added syntax for Ior like mentioned on issue #1539
This pull request includes ior syntax support for: