"Represents a value of one of two possible types (a disjoint union.) Instances of Either are either an instance of Left or Right.A common use of Either is as an alternative to Option for dealing with possible missing values. In this usage, scala.None is replaced with a Left which can contain useful information. Right takes the place of Some. Convention dictates that Left is used for failure and Right is used for success."
-1
The Unbiased Either is perfect for representing one of two values -
either the right or the left - without implying that one of them is
more "correct" than the other.
If there is bias towards right or left there is usually a good name
for that bias. For example Validation from Scalaz, and ParseResult
from the parser combinators. They are often fixed in one of the
parameters too, like Future.
That said, I do like the idea of a general right biased either, but I
really really don't want to sacrifice the unbiased one in in the
process.
"You should really use Maybe, not Either. I know all the books in print right now don't even mention it yet, but you'll just have to trust me until you come to understand for-comprehensions better, and monads"...Followed by the inevitable web search for monads and (more often than not) the resulting confusion followed by claims of Scala being too complicated and "academic". Then followed by the further inevitable repair of any damage so caused.
-1
I prefer unbiased Either. If we wanted to provide an implicit conversion to Option (via right projection) I doubt I'd mind, but Either can be used (and I do use it) to store two alternative things without bias. Adding bias will create a strong pressure for asymmetric code even though the conditions are symmetric.
If there is not an implicit conversion, I'd prefer a third class that was biased. If we wanted to avoid stepping on the Scalaz names, "Excused" might work.
--Rex
On Mon, May 14, 2012 at 3:08 AM, Jason Zaugg <jza...@gmail.com> wrote:(A follow-up from ticket SI-5793. [1])
Apologies in advance to any left-handers out there.
I propose (not that I'm the first) to right-bias Either. Concretely,
this means we would add map/flatMap etc directly to Either, rather
than going through RightProjection. RightProjection could probably
deprecated.
Is there any previous discussion on this? Any arguments against?
-jason
[1] https://issues.scala-lang.org/browse/SI-5793
Right-bias is not about being able to quickly convert to an Option, it's about being able to chain a sequence of operations, each of which takes the right output of the previous operation and yields an either.
Most especially, it's about being able to do so in the context of a for-comprehension.
Most especially, it's about being able to do so in the context of a for-comprehension.
Fair enough. But if this is the only thing it can do, I'm not sure it's worth it.
We introduced `Try` a while back along with SIP-14 (Futures). `Try` is analogous to `Either` with the exception that it's success-biased. That is, Either is symmetric, it has two type parameters that are wrapped (symmetrically) in either `Left` or `Right`. In general, by convention, `Either` can be used to represent a "successful" or a "failed" result, by representing some generic type in its right projection, and a subtype of Throwable in its left projection. However, this is by convention only, one can of course swap these projections (whether accidentally or purposefully) or one can represent two successful values in each projection if they so desire.`Try`, on the other hand, is parameterized only on one type, with this success/failure use case in mind. That is, the successful case is wrapped in `Success` (parameterized on T) while the failed case is a `Throwable` wrapped in `Failure`.`Try` comes from Twitter, who use it frequently in their codebase (see twitter/util on github), and who have found that it results in more readable and less error-prone code for this success/failure use case of `Either`.
How about doing the following:
* leave Either as it is, since the name doesn't imply any bias
(although Right certainly does), and it looks like changing it would
only buy you definitions in for-comprehensions (and not having to add
.right)
* introduce a completely new class, Checked, having OK and KO as the
type constructors
* include support for combining Checked values in both a fail-fast and
fail-slow manner, including applicative-functor support (<*>
operator), similar to that which I describe here:
http://robsscala.blogspot.co.uk/2012/05/validating-multiple-values-at-once.html
scala> implicit def doTheRightThing[A,B](x: Either[A,B]): Either.RightProjection[A,B] = x.rightdoTheRightThing: [A, B](x: Either[A,B])Either.RightProjection[A,B]scala> val one: Either[Int,String] = Right("Bippy")one: Either[Int,String] = Right(Bippy)scala> val two: Either[Int,String] = Left(42)two: Either[Int,String] = Left(42)scala> one map {_.toUpperCase}res0: Product with Either[Int,java.lang.String] with Serializable = Right(BIPPY)
scala> two map {_.toUpperCase}res1: Product with Either[Int,java.lang.String] with Serializable = Left(42)
I might agree with at least adding an import. (Especially if there was also a left-bias one could import.)
And I would _probably_ +1 the right-bias of Either as a whole (not just with an implicit) if I knew exactly what this meant. Right now it seems like we're giving +1 for something like "justice"--who can be against justice (or kittens)? But what exactly does it mean?
I'd rather show or reserve enthusiasm on the basis of an exact proposal (especially now that it's clearly established that the right-bias of Either would be widely accepted in principle).
In particular, I'm especially interested in how Option and Either are supposed to interact, in capabilities for recovering good states from error states and tossing away provisionally good states into error states, and in having complete (not partial) support for for-comprehensions.
For example, suppose we make Either look like RightProjection:
val e: Either[Int,String] = Right("fish")
for (x <- e.right if true) yield x
We get a deprecation warning about withFilter not existing, and with filter instead of getting the value we get neither an Option[String] nor an Either[_, String] but an Option[Either[Nothing,String]]. Wha? That's not very helpful for composition.
As another example, suppose I look something up in a map:
val m = Map("fish" -> true)
e.right.map(m.get)
Now I have an Either[Int, Option[Boolean]]. This is probably not what I want, and it's certainly not what for wants:
for (x <- e.right; y <- m.get(x)) yield y
// type mismatch; found: Option[Boolean] required: Either[?,?]
You can get this to work longhand
e.right.flatMap((m.get _) andThen {o => o.toRight(-1)})
but what a mess!
implicit def optionIsRight[T](x: Option[T]): Either[Nothing,T] = x map {Right(_)} getOrElse {Left(sys.err("Option lifted to Either has no Left type"))}
scala> implicit def optionIsRight[T](x: Option[T]): Either[Unit,T] = x map {Right(_)} getOrElse {Left(())}optionIsRight: [T](x: Option[T])Either[Unit,T]scala> for (x <- e.right; y <- m.get(x)) yield yres7: Either[AnyVal{def getClass(): java.lang.Class[_ >: Int with Unit <: AnyVal]},Boolean] = Right(true)
----------------------------------------
This message is intended exclusively for the individual(s) or entity to
which it is addressed. It may contain information that is proprietary,
privileged or confidential or otherwise legally exempt from disclosure.
If you are not the named addressee, you are not authorized to read,
print, retain, copy or disseminate this message or any part of it.
If you have received this message in error, please notify the sender
immediately by e-mail and delete all copies of the message.
And I would _probably_ +1 the right-bias of Either as a whole (not just with an implicit) if I knew exactly what this meant. Right now it seems like we're giving +1 for something like "justice"--who can be against justice (or kittens)? But what exactly does it mean?
That one's clearer. If Either is right-biased, then it makes much more sense to have an implicit conversion from Option[T] => Either[Nothing, T]
scala> implicit def optionIsRight[T](x: Option[T]): Either[Unit,T] = x map {Right(_)} getOrElse {Left(())}optionIsRight: [T](x: Option[T])Either[Unit,T]scala> for (x <- e.right; y <- m.get(x)) yield yres7: Either[AnyVal{def getClass(): java.lang.Class[_ >: Int with Unit <: AnyVal]},Boolean] = Right(true)
That kind of thinking gave use java.util.Data and then java.util.Calendar, just sayin'.
On 15 May 2012 16:27, Rex Kerr <ich...@gmail.com> wrote:I might agree with at least adding an import. (Especially if there was also a left-bias one could import.)
And I would _probably_ +1 the right-bias of Either as a whole (not just with an implicit) if I knew exactly what this meant. Right now it seems like we're giving +1 for something like "justice"--who can be against justice (or kittens)? But what exactly does it mean?
I'd rather show or reserve enthusiasm on the basis of an exact proposal (especially now that it's clearly established that the right-bias of Either would be widely accepted in principle).
In particular, I'm especially interested in how Option and Either are supposed to interact, in capabilities for recovering good states from error states and tossing away provisionally good states into error states, and in having complete (not partial) support for for-comprehensions.
For example, suppose we make Either look like RightProjection:
val e: Either[Int,String] = Right("fish")
for (x <- e.right if true) yield x
We get a deprecation warning about withFilter not existing, and with filter instead of getting the value we get neither an Option[String] nor an Either[_, String] but an Option[Either[Nothing,String]]. Wha? That's not very helpful for composition.
Definitely not pleasant. But also not an issue cased by biasing (or lack thereof), you'd get the same odd behaviour from both a biased Either and from an explicit right projection.The correct behaviour in this case is probably best left for a later discussion.
Right-bias is not about being able to quickly convert to an Option, it's about being able to chain a sequence of operations, each of which takes the right output of the previous operation and yields an either.
Most especially, it's about being able to do so in the context of a for-comprehension.
With access to the definition of Either + Left, we should be able to add a subclass of Left[Nothing] that could be used in exactly this scenario.
For now though, it can be faked. But the type inference ain't so pretty:scala> implicit def optionIsRight[T](x: Option[T]): Either[Unit,T] = x map {Right(_)} getOrElse {Left(())}optionIsRight: [T](x: Option[T])Either[Unit,T]scala> for (x <- e.right; y <- m.get(x)) yield yres7: Either[AnyVal{def getClass(): java.lang.Class[_ >: Int with Unit <: AnyVal]},Boolean] = Right(true)I'm still using .right of course. That would go away if Either were biased.
On Tuesday, May 15, 2012 12:27:20 PM UTC-4, Kevin Wright wrote:And I would _probably_ +1 the right-bias of Either as a whole (not just with an implicit) if I knew exactly what this meant. Right now it seems like we're giving +1 for something like "justice"--who can be against justice (or kittens)? But what exactly does it mean?
It means it's easier to use in the overwhelmingly common case. And not the least bit more difficult to use in any other case.
That one's clearer. If Either is right-biased, then it makes much more sense to have an implicit conversion from Option[T] => Either[Nothing, T]
It should be Option[T] => Either[Unit, T].scala> implicit def optionIsRight[T](x: Option[T]): Either[Unit,T] = x map {Right(_)} getOrElse {Left(())}optionIsRight: [T](x: Option[T])Either[Unit,T]scala> for (x <- e.right; y <- m.get(x)) yield yres7: Either[AnyVal{def getClass(): java.lang.Class[_ >: Int with Unit <: AnyVal]},Boolean] = Right(true)
This happens because you are unifying Either[Int, Boolean] with Either[Unit, Boolean]. You need to first turn the Int into a Unit:
for (x <- e.left.map(_ => ()).right; y <- m.get(x)) yield y
^
Can I throw my 2 penith worth behind keeping Either as the unbiassed product type and adding (if needed) an explicit biassed product with filter/map/... and two type constructors with names that clearly signal which is the success value and which is the failure value? I find the idea of using Either.Left for failure and Either.Right for success to be an obfuscated hack, precisely because the type constructors are called Left and Right, not Failure and Success. If the type is intended to be used with clear semantics then, by the ghost of Church, name the type after those semantics.
That's actually only the case with an import from a scalaz.
To be clear, my objection to ever-further biassing of Either is that the names are wrong. People understand intent of code through the names.
Well, that is a mistake. They should understand it through the types. Either is an extremely general kind of thing, so there is not going to be one good name that captures all intents. But the type certainly does.
A type that uses names that are clearly linked to their intended semantics doesn't seem to me to be at all about vague feelings of goodness. It is absolutely central to people being able to understand code.
Adding the following to the Scaladoc might do the trick: "By convention, the Left case may represent failure and the Right case may represent success."
trait Sum2[A, B]case class Sum2_Left[A, B](a: A) extends Sum2[A, B]case class Sum2_Right[A, B](b: B) extends Sum2[A, B]trait Try[A, B]case class Failure[A, B](a: A) extends Try[A, B]case class Success[A, B](b: B) extends Try[A, B]
I strongly believe that it is very important for names in code to meaningfully represent to people what the thing is representing.
It depends on what you think is important in general. Strong beliefs and names are not particularly important to me.
I agree another data type is warranted, but with careful consideration,we could unify them all with some nice library design with implicits --
just sayin'!
I have not seen in the proposal where Either now becomes biased toward>
failure or success. Right-biasing Either does not cause this.
On Wed, May 16, 2012 at 10:10 PM, Rex Kerr <ich...@gmail.com> wrote:
> I propose this as an alternative, with more favorable use characteristics
> than Try or right-biased Either.
If I understand it correctly, it's isomorphic to Either[Option[A], B],
which is nice in that it allows for a useful filter method.
Perhaps we could take a similar approach for Either:
class Either[A, B] {
def filter[AA](f: B => Boolean)(implicit ev: A =:= Option[AA]):
Either[A, B] =
this match {
case l @ Left(_) => l
case r @ Right(b) => if (f(b)) r else Left(None)
}
I would suggest to deprecate/rename the current filter methods in left
and right projection first.
Try is a different beast altogether, I think its transparent exception
> The one thing
> that Try does that Has does not is _almost_ consistently catch exceptions as
> they occur. (collect doesn't, for some odd reason--shouldn't like 146 be
> Try[U](...) instead of Success[U](...)?.)
handling is dangerous and currently inconsistent. See my comments here
[1]
Welcome to Scala version 2.9.2 (Java HotSpot(TM) Server VM, Java 1.6.0_18).Type in expressions to have them evaluated.Type :help for more information.scala> import scalaz._; import Scalaz._import scalaz._import Scalaz._scala> "1".success[Exception] flatMap (_.parseInt)res0: scalaz.Validation[Exception,Int] = Success(1)
scala> for {| s <- "1".success[Exception]| i <- s.parseInt| }| yield ires1: scalaz.Validation[Exception,Int] = Success(1)
scala> "1".success[Exception] >>= (_.parseInt)<console>:14: error: could not find implicit value for parameter b: scalaz.Bind[[α]scalaz.Validation[Exception,α]]"1".success[Exception] >>= (_.parseInt)^
Chrisscala> import Validation.Monad._import Validation.Monad._scala> "1".success[Exception] >>= (_.parseInt)res3: scalaz.Validation[Exception,Int] = Success(1)
On May 18, 2012 3:36 AM, "Chris Marshall" <oxbow...@hotmail.com> wrote:
>
> Scalaz's Validation does have flatMap.
This will be fixed. Please assume it doesn't.
Validation.flatMap should be a type error.
It's not a monad, such that it obeys any relationship to its applicative functor.
Imagine if flatMap(point compose f) != map(f) for example.
scala> import scalaz._; import Scalaz._import scalaz._import Scalaz._
scala> type VS[a]=Validation[String, a]defined type alias VSscala> def point[A](a: A): VS[A] = a.successpoint: [A](a: A)VS[A]scala> def m[A, B](vs: VS[A])(f: A => B): VS[B] = vs map fm: [A, B](vs: VS[A])(f: A => B)VS[B]scala> def fm[A, B](vs: VS[A])(f: A => VS[B]): VS[B] = vs flatMap ffm: [A, B](vs: VS[A])(f: A => VS[B])VS[B]
scala> val f: String => VS[Int] = s => try { s.toInt.success } catch { case x => x.getMessage.fail }f: String => VS[Int] = <function1>
scala> fm("1".success)(f andThen point)res6: VS[VS[Int]] = Success(Success(1))scala> m("1".success)(f)res7: VS[VS[Int]] = Success(Success(1))
scala> fm("a".success)(f andThen point)res8: VS[VS[Int]] = Success(Failure(For input string: "a"))scala> m("a".success)(f)res9: VS[VS[Int]] = Success(Failure(For input string: "a"))scala> fm("x".fail)(f andThen point)res15: VS[VS[Int]] = Failure(x)scala> m("x".fail)(f)res16: VS[VS[Int]] = Failure(x)
scala> val g: String => Int = _.lengthg: String => Int = <function1>scala> fm("a".success)(g andThen point)res10: VS[Int] = Success(1)scala> m("a".success)(g)res12: VS[Int] = Success(1)scala> fm("a".fail)(g andThen point)res13: VS[Int] = Failure(a)scala> m("a".fail)(g)res14: VS[Int] = Failure(a)
scala> val List(a, b, c) = List(Success(2), Failure("foo"), Failure("bar")) : List[Validation[String, Int]]a: scalaz.Validation[String,Int] = Success(2)b: scalaz.Validation[String,Int] = Failure(foo)c: scalaz.Validation[String,Int] = Failure(bar)scala> (a |@| b |@| c)(_ + _ + _)res2: scalaz.Validation[String,Int] = Failure(foobar)
scala> import Validation.Monad._import Validation.Monad._
scala> (a |@| b |@| c)(_ + _ + _)res3: scalaz.Validation[String,Int] = Failure(foo)
On May 19, 2012 1:49 AM, "Runar Bjarnason" <runar...@gmail.com> wrote:
>
> validation.flatMap(f) is simply shorthand for validation.toEither.flatMap(f).toValidation
Actually it's the same as:
validation.toEither.flatMap(f andThen (_.toEither)).toValidation.
I agree this is a fine thing and only object to the method being called flatMap or the operation being called a monad. It is not.
(A follow-up from ticket SI-5793. [1])
Apologies in advance to any left-handers out there.
I propose (not that I'm the first) to right-bias Either. Concretely,
this means we would add map/flatMap etc directly to Either, rather
than going through RightProjection. RightProjection could probably
deprecated.
Is there any previous discussion on this? Any arguments against?
-jason
[1] https://issues.scala-lang.org/browse/SI-5793
scala> for {| str <- Option("1")| i <- intOpt(str) //intOpt is String => Option[String]| val j = i + 10 //Note use of = in generator| }| yield jres18: Option[Int] = Some(11)
Chris, Jason, All,
How about just fixing the problem in the bug report*, which I think
I've just managed to do:
http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html
* https://issues.scala-lang.org/browse/SI-5793
On Thu, Jun 7, 2012 at 7:02 PM, Rex Kerr <ich...@gmail.com> wrote:Rex, Please could you point me to an example that demonstrates why it's awkward.
> It is a little awkward to simply say, "Don't use if in your for statements".
>
> This is why when I wrote Has, I gave it three states (isomorphic to
> Either[Option[A],B]).
>Sorry, I don't understand what you mean.
> Personally, I found working without a generic reason to be awkward.