From d329bdc2a8394dc01a31022eb27d6136e16be2c9 Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Tue, 12 Jun 2012 21:28:46 +0100 Subject: [PATCH 01/16] added first draught of sip, which although fairly complete, could do with some example code, and has yet to be checked using jekyll --- ...ing-eithers-for-comprehension-behaviour.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 sips/pending/_posts/fixing-eithers-for-comprehension-behaviour.md diff --git a/sips/pending/_posts/fixing-eithers-for-comprehension-behaviour.md b/sips/pending/_posts/fixing-eithers-for-comprehension-behaviour.md new file mode 100644 index 0000000000..9242d2f062 --- /dev/null +++ b/sips/pending/_posts/fixing-eithers-for-comprehension-behaviour.md @@ -0,0 +1,58 @@ +--- +layout: sip +disqus: true +title: SIP-20 Fixing Either's for-comprehension behaviour +--- + +**By: Rob Dickens** + +## Motivation ## + +There is [evidence][evidence] to suggest that `scala.Either` is being +eschewed in favour of alternatives such as Scalaz's `Validation` +class. Based on this, together with a subsequent study which resulted in +[this suggestion][suggestion], the following three reasons may be +considered likely. + +1. The alternatives are simpler to use, since they are 'biased' + towards one of their two possible types of result - in the case of + `Either`, it is necessary to specify that it's the `Left` (or + `Right`) instance whose value will be passed to `foreach`, `map` or + `flatMap`. + +2. `for` comprehensions involving `Either` do not always behave as + expected! + +3. The alternative may offer 'added value', such as `Validation`'s + ability to accumulate failures. + +The current SIP is a proposal to address reason 2. only. (It may also +be considered a proposal to leave `Either` unbiased, on the grounds that +its name does not imply any bias towards either of it's subtypes.) + +The aforementioned study traced the unexpected behaviour in +`for` comprehensions to two specific aspects of the API of `Either`'s +`Left/RightProjection` inner classes, and the two corrections that were +[suggested][suggestion] are now being proposed here. + +## 1. map should return a Left/RightProjection instead of an Either ## + +Returning an `Either` means that definitions in `for` comprehensions are +not supported, since `Either` does not have a `map` method. Instead, +`map` should return a `Left/RightProjection`, which does have a `map` method, +together with a `foreach` and `flatMap`. + +## 2. filter should be removed ## + +This is not appropriate, since an empty result is not appropriate here +- either it's a `Left` or it's a `Right`. + +If the, say, `Right` value in question is to be subject to a +condition, then it should first be transformed into a `scala.Option`, +using an `R => Option[R]` (for use with `map`) or `R => Either[L, +Option[R]]` (for use with `flatMap`), so as to retain the opportunity of +handling a `Left` result. + + [evidence]: http://robsscala.blogspot.co.uk/2012/04/validation-without-scalaz.html + [suggestion]: + http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html From 66ba0b673ab37196d1bae37ef936773e3c3c4ee1 Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Wed, 13 Jun 2012 10:52:54 +0100 Subject: [PATCH 02/16] forgot to prepend date to sip filename --- ...d => 2012-06-13-fixing-eithers-for-comprehension-behaviour.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sips/pending/_posts/{fixing-eithers-for-comprehension-behaviour.md => 2012-06-13-fixing-eithers-for-comprehension-behaviour.md} (100%) diff --git a/sips/pending/_posts/fixing-eithers-for-comprehension-behaviour.md b/sips/pending/_posts/2012-06-13-fixing-eithers-for-comprehension-behaviour.md similarity index 100% rename from sips/pending/_posts/fixing-eithers-for-comprehension-behaviour.md rename to sips/pending/_posts/2012-06-13-fixing-eithers-for-comprehension-behaviour.md From 59841bafae0cf4d7293df091472119daafda3b09 Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Wed, 13 Jun 2012 17:06:41 +0100 Subject: [PATCH 03/16] sip file renamed to 2012-06-13-fixing-eithers-left-rightprojection.md --- ...ing-eithers-for-comprehension-behaviour.md | 58 ------------- ...-13-fixing-eithers-left-rightprojection.md | 87 +++++++++++++++++++ 2 files changed, 87 insertions(+), 58 deletions(-) delete mode 100644 sips/pending/_posts/2012-06-13-fixing-eithers-for-comprehension-behaviour.md create mode 100644 sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md diff --git a/sips/pending/_posts/2012-06-13-fixing-eithers-for-comprehension-behaviour.md b/sips/pending/_posts/2012-06-13-fixing-eithers-for-comprehension-behaviour.md deleted file mode 100644 index 9242d2f062..0000000000 --- a/sips/pending/_posts/2012-06-13-fixing-eithers-for-comprehension-behaviour.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -layout: sip -disqus: true -title: SIP-20 Fixing Either's for-comprehension behaviour ---- - -**By: Rob Dickens** - -## Motivation ## - -There is [evidence][evidence] to suggest that `scala.Either` is being -eschewed in favour of alternatives such as Scalaz's `Validation` -class. Based on this, together with a subsequent study which resulted in -[this suggestion][suggestion], the following three reasons may be -considered likely. - -1. The alternatives are simpler to use, since they are 'biased' - towards one of their two possible types of result - in the case of - `Either`, it is necessary to specify that it's the `Left` (or - `Right`) instance whose value will be passed to `foreach`, `map` or - `flatMap`. - -2. `for` comprehensions involving `Either` do not always behave as - expected! - -3. The alternative may offer 'added value', such as `Validation`'s - ability to accumulate failures. - -The current SIP is a proposal to address reason 2. only. (It may also -be considered a proposal to leave `Either` unbiased, on the grounds that -its name does not imply any bias towards either of it's subtypes.) - -The aforementioned study traced the unexpected behaviour in -`for` comprehensions to two specific aspects of the API of `Either`'s -`Left/RightProjection` inner classes, and the two corrections that were -[suggested][suggestion] are now being proposed here. - -## 1. map should return a Left/RightProjection instead of an Either ## - -Returning an `Either` means that definitions in `for` comprehensions are -not supported, since `Either` does not have a `map` method. Instead, -`map` should return a `Left/RightProjection`, which does have a `map` method, -together with a `foreach` and `flatMap`. - -## 2. filter should be removed ## - -This is not appropriate, since an empty result is not appropriate here -- either it's a `Left` or it's a `Right`. - -If the, say, `Right` value in question is to be subject to a -condition, then it should first be transformed into a `scala.Option`, -using an `R => Option[R]` (for use with `map`) or `R => Either[L, -Option[R]]` (for use with `flatMap`), so as to retain the opportunity of -handling a `Left` result. - - [evidence]: http://robsscala.blogspot.co.uk/2012/04/validation-without-scalaz.html - [suggestion]: - http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html diff --git a/sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md b/sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md new file mode 100644 index 0000000000..2cd4954880 --- /dev/null +++ b/sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md @@ -0,0 +1,87 @@ +--- +layout: sip +disqus: true +title: SIP-20 Fixing Either's Left/RightProjection +--- + +**By: Rob Dickens** + +This proposal is the sole initiative of the author, based on his +attempts, [first][enhance] to understand why `scala.Either` is not more widely +used, and to enhance it, [and then][fix] to try to fix it. It is also being +made in the light of this [debate][debate] which discussed possible changes and +alternatives to the class. + +## Motivation ## + +The above points to the following three reasons why Either is being +eschewed in favour of alternatives: + +1. `for` comprehensions involving `Either` have unexpected limitations + and behave in a non-standard way. + +2. The alternatives are simpler to use, by virtue of being 'biased' + towards one of their possible result types. In the case of + `Either`, it is necessary to specify which of the two types (Left + or Right) should have its value passed to `foreach`, `map` or + `flatMap`. + +3. The alternative may offer 'added value', such as `scalaz.Validation`'s + ability to accumulate failures. + +The current SIP is a proposal to address 1. only, and retain the +ability to choose from which side the value passed to `foreach`, +etc. will be taken. This is on the grounds that, + +* there might be valid use-cases which require the ability to choose + +* it addresses 1. while leaving the way open to address 2. and 3. in + the future, whether by making `Either` biased, or by introducing a + biased alternative to go along side it. + +The proposed solution to 1. is the one advocated by the above +[fix][fix], and involves two changes to Either's Left/RightProject +classes. + +## Part 1: map should return a Left/RightProjection instead of an Either ## + +Returning an `Either` means that definitions in `for` comprehensions are +not supported, since `Either` does not have a `map` method. Instead, +`map` should return a `Left/RightProjection`, which does have a `map` method, +together with a `foreach` and `flatMap`. + +Since `yield` would then produce a `Left/RightProjection`, `.e` will +need to be appended in order to obtain the `Either`. + +## Part 2: filter should be removed outright ## + +Filtering is not appropriate here, since an empty result is not +appropriate here - either it's a `LeftProjection` or it's a +`RightProjection`. + +If the value in the, say, `Right` instance is to be subject to a +condition, then it should first be lifted into a `scala.Option`, using +an `R => Option[R]` (for use with `map`) or an `Option` inside a +`RightProjection`, using an `R => RightProjection[L, Option[R]]` (for +use with `flatMap`), so as to retain the opportunity of handling a +`Left` result. + +The presence of the current `filter: Option[Either[...]]` has +the following negative consequences: + +* compiler warning that there is no `withFilter` + +* the fact that it returns an Option as opposed to a + Left-/RightProjection is unexpected, and leads to a compilation + error if `if` is used after two generators + +* the opportunity to handle the other projection is lost. + +Note that simply deprecating `filter` will not be enough to prevent it +being used in `for` comprehensions. + + [enhance]: http://robsscala.blogspot.co.uk/2012/04/validation-without-scalaz.html + [fix]: + http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html + [debate]: + https://groups.google.com/group/scala-debate/browse_thread/thread/2bac2fe8aa6124ad?hl=en From 138066041e77f02b73180b07c7f7f8642b652784 Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Mon, 25 Jun 2012 10:27:12 +0100 Subject: [PATCH 04/16] !20120-06-12-fixing-eithers-left-rightprojection.md: various small improvements --- ...-13-fixing-eithers-left-rightprojection.md | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md b/sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md index 2cd4954880..084565892a 100644 --- a/sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md +++ b/sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md @@ -1,7 +1,7 @@ --- layout: sip disqus: true -title: SIP-20 Fixing Either's Left/RightProjection +title: SIP-20 Fixing Either's Left-/RightProjection --- **By: Rob Dickens** @@ -14,22 +14,22 @@ alternatives to the class. ## Motivation ## -The above points to the following three reasons why Either is being +The above findings point to the following three reasons why `Either` is being eschewed in favour of alternatives: 1. `for` comprehensions involving `Either` have unexpected limitations and behave in a non-standard way. -2. The alternatives are simpler to use, by virtue of being 'biased' +2. The alternatives are simpler to use, by virtue of their being 'biased' towards one of their possible result types. In the case of - `Either`, it is necessary to specify which of the two types (Left - or Right) should have its value passed to `foreach`, `map` or + `Either`, it is necessary to specify which of the two types (`Left` + or `Right`) should have its value passed to `foreach`, `map` or `flatMap`. 3. The alternative may offer 'added value', such as `scalaz.Validation`'s ability to accumulate failures. -The current SIP is a proposal to address 1. only, and retain the +The current SIP is a proposal to address 1. only, and to retain the ability to choose from which side the value passed to `foreach`, etc. will be taken. This is on the grounds that, @@ -39,43 +39,46 @@ etc. will be taken. This is on the grounds that, the future, whether by making `Either` biased, or by introducing a biased alternative to go along side it. -The proposed solution to 1. is the one advocated by the above -[fix][fix], and involves two changes to Either's Left/RightProject -classes. +The proposed approach is the one advocated by the above [fix][fix], +and involves two changes to `Either`'s `Left-/RightProject` classes. -## Part 1: map should return a Left/RightProjection instead of an Either ## +## Part 1: map should return a Left-/RightProjection instead of an Either ## -Returning an `Either` means that definitions in `for` comprehensions are -not supported, since `Either` does not have a `map` method. Instead, -`map` should return a `Left/RightProjection`, which does have a `map` method, -together with a `foreach` and `flatMap`. +Returning an `Either` means that definitions in `for` comprehensions +are not supported, since `Either` does not have a `map` method (as +pointed out in this [bug report][report]). Instead, it is proposed +that `map` should return a `Left-/RightProjection`, which does have the +requisite method. -Since `yield` would then produce a `Left/RightProjection`, `.e` will -need to be appended in order to obtain the `Either`. +Since `yield` would then also produce a `Left-/RightProjection`, `.e` +will need to be appended in order to obtain the `Either`. -## Part 2: filter should be removed outright ## +## Part 2: filter should be removed (outright) ## Filtering is not appropriate here, since an empty result is not -appropriate here - either it's a `LeftProjection` or it's a +appropriate - either it's a `LeftProjection` or it's a `RightProjection`. If the value in the, say, `Right` instance is to be subject to a condition, then it should first be lifted into a `scala.Option`, using an `R => Option[R]` (for use with `map`) or an `Option` inside a `RightProjection`, using an `R => RightProjection[L, Option[R]]` (for -use with `flatMap`), so as to retain the opportunity of handling a +use with `flatMap`), so as to retain the ability to deal with a `Left` result. The presence of the current `filter: Option[Either[...]]` has the following negative consequences: -* compiler warning that there is no `withFilter` +* uses of `if` in `for` comprehensions trigger compiler warnings that + there is no `withFilter` -* the fact that it returns an Option as opposed to a - Left-/RightProjection is unexpected, and leads to a compilation - error if `if` is used after two generators +* the fact that `filter` returns an `Option` as opposed to a + `Left-/RightProjection` is unexpected, and leads to a compilation + error if `if` is used after multiple generators together with + `yield` -* the opportunity to handle the other projection is lost. +* if the result type is the other one, the ability to deal with it + is lost. Note that simply deprecating `filter` will not be enough to prevent it being used in `for` comprehensions. @@ -85,3 +88,4 @@ being used in `for` comprehensions. http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html [debate]: https://groups.google.com/group/scala-debate/browse_thread/thread/2bac2fe8aa6124ad?hl=en + [report]: https://issues.scala-lang.org/browse/SI-5793 From 467cdc9a23b241df5813c082423c91037f187919 Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Thu, 28 Jun 2012 12:31:31 +0100 Subject: [PATCH 05/16] +sips/pending/_posts/2012-06-28-fixing-either.md --- .../_posts/2012-06-28-fixing-either.md | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 sips/pending/_posts/2012-06-28-fixing-either.md diff --git a/sips/pending/_posts/2012-06-28-fixing-either.md b/sips/pending/_posts/2012-06-28-fixing-either.md new file mode 100644 index 0000000000..e4cb1b4a83 --- /dev/null +++ b/sips/pending/_posts/2012-06-28-fixing-either.md @@ -0,0 +1,156 @@ +--- +layout: sip +disqus: true +title: SIP-20 Fixing Either +--- + +**By: Rob Dickens** + +This proposal is the sole initiative of the author, based on his +attempts, [first][enhance] to understand why `scala.Either` is not +more widely used, and to enhance it, [and then][fix] to try to fix +it. It is also being made in the light of two mailing-list debates +which have taken place - one about [right-biasing Either][debate1], +which was inconclusive, and a subsequent one about [fixing +Either][debate2]. + +## Motivation ## + +The above findings point to the following three reasons why `Either` is being +eschewed in favour of alternatives: + +1. `for` comprehensions involving `Either` behave oddly. + +2. The alternatives are simpler to use, by virtue of their being 'biased' + towards one of their possible result types. In the case of + `Either`, it is necessary to specify which of the two types (`Left` + or `Right`) should have its value passed to `foreach`, `map` or + `flatMap`. + +3. The alternative may offer 'added value', such as `scalaz.Validation`'s + ability to accumulate failures. + +The current SIP is a proposal to address 1. and 2. only. + +## Part 1: Eliminating odd behaviour in for-comprehensions ## + +Two examples of odd behaviour have been encountered. Firstly, +definitions are not supported (as first reported [here][report]), + + for { + b <- gt0(a).right + // ^ + // error: value foreach is not a member of + // Product with Serializable with Either[String,(Int, Int)] + c = b + 1 + } ... // do something with c + +and secondly (as reported [here][fix]), `if` cannot be used together +with multiple generators and `yield`: + + for { + b <- gt0(a).right + c <- gt1(b).right + // ^ + // error: type mismatch; + // found : Option[Either[Nothing,Int]] + // required: Either[?,?] + if c > 0 + } yield c + +The lack of support for definitions was traced to the fact that the +`map` method of LeftProjection and RightProjection returns an +`Either`, which does not have a `foreach`, or `map` method itself. + +The proposed solution is the one proposed [here][fix], whereby the +`map` method of `LeftProjection` returns another `LeftProjection`, and +that of `RightProjection` returns another `RightProjection`. + +Also, and as a conseqence, the respective `flatMap` methods must be +changed by substituting the respective projection in place of +`Either`. + +Since the above changes may not be made by simply deprecating the old +versions and adding the new, it will be necessary to deprecate and +replace `LeftProjection` and `RightProjection` themselves, and therefore also +`Either`'s `left` and `right` methods. + +The proposed alternatives are `LeftProj` and `RightProj`, as returned +by `lp` and `rp`. + +Therefore, in the case of `LeftProj`, + + def map[X](f: A => X) = e match { + case Left(a) => Left(f(a)) + case Right(b) => Right(b) + } + +becomes + + def map[X](f: A => X): LeftProj[X, B] = e match { + case Left(a) => LeftProj(Left(f(a))) + case Right(b) => LeftProj(Right(b)) + } + +and + + def flatMap[BB >: B, X](f: A => Either[X, BB]) = e match { + case Left(a) => f(a) + case Right(b) => Right(b) + } + +becomes + + def flatMap[BB >: B, X](f: A => LeftProj[X, BB]): LeftProj[X, BB] = e match { + case Left(a) => f(a) + case Right(b) => LeftProj(Right(b)) + } + +Note that `.e` must be appended to the value the for-comprehension then +yields, in order to obtain the corresponding value of `Either`. + +Regarding the second example of odd behaviour, involving if, this was +traced to the fact that the filter method (of `LeftProjection` and +`RightProjection`) returns an `Option` instead of the respective +projection. + +Considering that an `Either` must be (by definition) either a `Left` +or a `Right` (and not empty), it is proposed that `LeftProj` and +`RightProj` should not have a `filter` (or `withFilter`) method at +all. + +Note that this means that + + +## Part 2: Simplifying use in for-comprehensions by *adding* right-biased capability ## + +Returning an `Either` means that definitions in `for` comprehensions +are not supported, since `Either` does not have a `map` method (as +pointed out in this [bug report][report]). Instead, it is proposed +that `map` should return a `Left-/RightProjection`, which does have the +requisite method. + +Since `yield` would then also produce a `Left-/RightProjection`, `.e` +will need to be appended in order to obtain the `Either`. + +## Part 2: filter should be removed (outright) ## + +Filtering is not appropriate here, since an empty result is not +appropriate - either it's a `LeftProjection` or it's a +`RightProjection`. + +If the value in the, say, `Right` instance is to be subject to a +condition, then it should first be lifted into a `scala.Option`, using +an `R => Option[R]` (for use with `map`) or an `Option` inside a +`RightProjection`, using an `R => RightProjection[L, Option[R]]` (for +use with `flatMap`), so as to retain the ability to deal with a +`Left` result. + + [enhance]: http://robsscala.blogspot.co.uk/2012/04/validation-without-scalaz.html + [fix]: + http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html + [debate1]: + https://groups.google.com/group/scala-debate/browse_thread/thread/2bac2fe8aa6124ad?hl=en + [debate2]: + https://groups.google.com/forum/?fromgroups#!topic/scala-debate/XlN-oqbslS0 + [report]: https://issues.scala-lang.org/browse/SI-5793 From 6b33da2bd93559690511216f90e6906f5ee4b1ca Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Fri, 29 Jun 2012 11:18:17 +0100 Subject: [PATCH 06/16] sips/pending/_posts/2012-06-{-28 +29}-fixing-either.md: Part 2 now added --- ...-either.md => 2012-06-29-fixing-either.md} | 87 +++++++++++++------ 1 file changed, 62 insertions(+), 25 deletions(-) rename sips/pending/_posts/{2012-06-28-fixing-either.md => 2012-06-29-fixing-either.md} (60%) diff --git a/sips/pending/_posts/2012-06-28-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md similarity index 60% rename from sips/pending/_posts/2012-06-28-fixing-either.md rename to sips/pending/_posts/2012-06-29-fixing-either.md index e4cb1b4a83..e346cd2640 100644 --- a/sips/pending/_posts/2012-06-28-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -107,44 +107,81 @@ becomes } Note that `.e` must be appended to the value the for-comprehension then -yields, in order to obtain the corresponding value of `Either`. +yields, in order to obtain the corresponding `Either` value. -Regarding the second example of odd behaviour, involving if, this was +Regarding the second example of odd behaviour, involving `if`, this was traced to the fact that the filter method (of `LeftProjection` and `RightProjection`) returns an `Option` instead of the respective projection. -Considering that an `Either` must be (by definition) either a `Left` -or a `Right` (and not empty), it is proposed that `LeftProj` and -`RightProj` should not have a `filter` (or `withFilter`) method at -all. +The first solution considered was to introduce a third subtype, +equivalent to `Option`'s `None`, but this was later rejected on the +grounds that only two subtypes may exist - *either* `Left` or `Right`. -Note that this means that +The second solution considered was to do away with `filter` +altogether, given that there must be *some* result (either `Left` or +`Right`). However, this would prevent the general use of `if` and +pattern-matching in `for` comprehensions involving (projections of) +`Either`. +Therefore, a third solution has been investigated, whereby `LeftProj` +(`RightProj`) has a `withFilter` method that returns a `LeftProj` +containing a `Right` (`RightProj` containing a `Left`) if the +predicate is `false`, and where the contents of that `Right` (`Left`) +is obtained using an implicit conversion passed to the `withFilter` +method in a second parameter list: + + def withFilter[BB >: B](p: A => Boolean) + (implicit aToB: Right.Convert => BB): LeftProj[A, BB] = { + val e2: Either[A, BB] = e match { + case Left(a) => if (p(a)) Left(a) else Right(aToB(Right.Convert(a))) + case Right(b) => Right(b) + } + LeftProj(e2) + } -## Part 2: Simplifying use in for-comprehensions by *adding* right-biased capability ## +This solution therefore requires that an implicit conversion such as +the following be provided whenever the method is used, as is the case +when `if` or pattern-matching features in a `for` comprehension: -Returning an `Either` means that definitions in `for` comprehensions -are not supported, since `Either` does not have a `map` method (as -pointed out in this [bug report][report]). Instead, it is proposed -that `map` should return a `Left-/RightProjection`, which does have the -requisite method. + implicit def f(convert: Right.Convert) = convert.any.toString -Since `yield` would then also produce a `Left-/RightProjection`, `.e` -will need to be appended in order to obtain the `Either`. +Note that `Convert` is a simple case class which serves to ensure that +the implicit conversion is properly targetted. -## Part 2: filter should be removed (outright) ## +This third solution has been demonstrated to work well, and is the one +proposed here. -Filtering is not appropriate here, since an empty result is not -appropriate - either it's a `LeftProjection` or it's a -`RightProjection`. +## Part 2: Simplifying use in for-comprehensions by *adding* right-biased capability ## + +It is proposed that the various methods required for supporting `for` +comprehensions be added to `Either` itself, and that only the value +contained in `Right` instances be passed to the functions passed to +those methods. + +This would simplify the vast majority of use cases, and be in keeping +with the existing convention of using `for` comprehensions involving +the `RightProjection`: + + trait Eg { + def f(a: Int): Either[String, Int] + + def unbiased_usage(a: Int) = { + val rp = for { + b <- f(a).rp + } yield b + rp.e + } + + def rightBiased_usaged(a: Int) = for { + b <- f(a) + } yield b + } -If the value in the, say, `Right` instance is to be subject to a -condition, then it should first be lifted into a `scala.Option`, using -an `R => Option[R]` (for use with `map`) or an `Option` inside a -`RightProjection`, using an `R => RightProjection[L, Option[R]]` (for -use with `flatMap`), so as to retain the ability to deal with a -`Left` result. +Finally, note that Part 2 does not render Part 1 redundant; although +`Either` would now have its own `map` method, this would only be +appropriate in `for` comprehensions involving the `RightProjection`, +but not the `LeftProjection`. [enhance]: http://robsscala.blogspot.co.uk/2012/04/validation-without-scalaz.html [fix]: From c60a6a9cefa6e9ebfff4c339089ea959f6a5a4ee Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Fri, 29 Jun 2012 12:37:00 +0100 Subject: [PATCH 07/16] !sips/pending/_posts/2012-06-29}-fixing-either.md: should now be ready to submit -sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md --- ...-13-fixing-eithers-left-rightprojection.md | 91 ------------------- .../_posts/2012-06-29-fixing-either.md | 48 ++++++---- 2 files changed, 28 insertions(+), 111 deletions(-) delete mode 100644 sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md diff --git a/sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md b/sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md deleted file mode 100644 index 084565892a..0000000000 --- a/sips/pending/_posts/2012-06-13-fixing-eithers-left-rightprojection.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -layout: sip -disqus: true -title: SIP-20 Fixing Either's Left-/RightProjection ---- - -**By: Rob Dickens** - -This proposal is the sole initiative of the author, based on his -attempts, [first][enhance] to understand why `scala.Either` is not more widely -used, and to enhance it, [and then][fix] to try to fix it. It is also being -made in the light of this [debate][debate] which discussed possible changes and -alternatives to the class. - -## Motivation ## - -The above findings point to the following three reasons why `Either` is being -eschewed in favour of alternatives: - -1. `for` comprehensions involving `Either` have unexpected limitations - and behave in a non-standard way. - -2. The alternatives are simpler to use, by virtue of their being 'biased' - towards one of their possible result types. In the case of - `Either`, it is necessary to specify which of the two types (`Left` - or `Right`) should have its value passed to `foreach`, `map` or - `flatMap`. - -3. The alternative may offer 'added value', such as `scalaz.Validation`'s - ability to accumulate failures. - -The current SIP is a proposal to address 1. only, and to retain the -ability to choose from which side the value passed to `foreach`, -etc. will be taken. This is on the grounds that, - -* there might be valid use-cases which require the ability to choose - -* it addresses 1. while leaving the way open to address 2. and 3. in - the future, whether by making `Either` biased, or by introducing a - biased alternative to go along side it. - -The proposed approach is the one advocated by the above [fix][fix], -and involves two changes to `Either`'s `Left-/RightProject` classes. - -## Part 1: map should return a Left-/RightProjection instead of an Either ## - -Returning an `Either` means that definitions in `for` comprehensions -are not supported, since `Either` does not have a `map` method (as -pointed out in this [bug report][report]). Instead, it is proposed -that `map` should return a `Left-/RightProjection`, which does have the -requisite method. - -Since `yield` would then also produce a `Left-/RightProjection`, `.e` -will need to be appended in order to obtain the `Either`. - -## Part 2: filter should be removed (outright) ## - -Filtering is not appropriate here, since an empty result is not -appropriate - either it's a `LeftProjection` or it's a -`RightProjection`. - -If the value in the, say, `Right` instance is to be subject to a -condition, then it should first be lifted into a `scala.Option`, using -an `R => Option[R]` (for use with `map`) or an `Option` inside a -`RightProjection`, using an `R => RightProjection[L, Option[R]]` (for -use with `flatMap`), so as to retain the ability to deal with a -`Left` result. - -The presence of the current `filter: Option[Either[...]]` has -the following negative consequences: - -* uses of `if` in `for` comprehensions trigger compiler warnings that - there is no `withFilter` - -* the fact that `filter` returns an `Option` as opposed to a - `Left-/RightProjection` is unexpected, and leads to a compilation - error if `if` is used after multiple generators together with - `yield` - -* if the result type is the other one, the ability to deal with it - is lost. - -Note that simply deprecating `filter` will not be enough to prevent it -being used in `for` comprehensions. - - [enhance]: http://robsscala.blogspot.co.uk/2012/04/validation-without-scalaz.html - [fix]: - http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html - [debate]: - https://groups.google.com/group/scala-debate/browse_thread/thread/2bac2fe8aa6124ad?hl=en - [report]: https://issues.scala-lang.org/browse/SI-5793 diff --git a/sips/pending/_posts/2012-06-29-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md index e346cd2640..e71b79ee9e 100644 --- a/sips/pending/_posts/2012-06-29-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -8,11 +8,11 @@ title: SIP-20 Fixing Either This proposal is the sole initiative of the author, based on his attempts, [first][enhance] to understand why `scala.Either` is not -more widely used, and to enhance it, [and then][fix] to try to fix -it. It is also being made in the light of two mailing-list debates -which have taken place - one about [right-biasing Either][debate1], -which was inconclusive, and a subsequent one about [fixing -Either][debate2]. +more widely used, and to enhance it, and then ([here][fix] and +[here][vs]) to try to fix it. It is also being made in the light of +two mailing-list debates which have taken place - one (initiated by +Jason Zaugg) about [right-biasing Either][debate1], which was +inconclusive, and a subsequent one about [fixing Either][debate2]. ## Motivation ## @@ -21,11 +21,11 @@ eschewed in favour of alternatives: 1. `for` comprehensions involving `Either` behave oddly. -2. The alternatives are simpler to use, by virtue of their being 'biased' - towards one of their possible result types. In the case of +2. The alternatives are simpler to use, by virtue of their being + 'biased' towards one of their possible result types. In the case of `Either`, it is necessary to specify which of the two types (`Left` - or `Right`) should have its value passed to `foreach`, `map` or - `flatMap`. + or `Right`) should have its value passed to the function passed to + `foreach`, `map` or `flatMap`. 3. The alternative may offer 'added value', such as `scalaz.Validation`'s ability to accumulate failures. @@ -45,7 +45,7 @@ definitions are not supported (as first reported [here][report]), c = b + 1 } ... // do something with c -and secondly (as reported [here][fix]), `if` cannot be used together +and secondly (as mentioned [here][fix]), `if` cannot be used together with multiple generators and `yield`: for { @@ -59,12 +59,12 @@ with multiple generators and `yield`: } yield c The lack of support for definitions was traced to the fact that the -`map` method of LeftProjection and RightProjection returns an +`map` method of `LeftProjection` and `RightProjection` returns an `Either`, which does not have a `foreach`, or `map` method itself. The proposed solution is the one proposed [here][fix], whereby the -`map` method of `LeftProjection` returns another `LeftProjection`, and -that of `RightProjection` returns another `RightProjection`. +`map` method of `LeftProjection` (`RightProjection`) returns another +`LeftProjection` (`RightProjection`). Also, and as a conseqence, the respective `flatMap` methods must be changed by substituting the respective projection in place of @@ -106,7 +106,7 @@ becomes case Right(b) => LeftProj(Right(b)) } -Note that `.e` must be appended to the value the for-comprehension then +Note that `.e` must be appended to the value the `for` comprehension then yields, in order to obtain the corresponding `Either` value. Regarding the second example of odd behaviour, involving `if`, this was @@ -126,10 +126,10 @@ pattern-matching in `for` comprehensions involving (projections of) Therefore, a third solution has been investigated, whereby `LeftProj` (`RightProj`) has a `withFilter` method that returns a `LeftProj` -containing a `Right` (`RightProj` containing a `Left`) if the -predicate is `false`, and where the contents of that `Right` (`Left`) -is obtained using an implicit conversion passed to the `withFilter` -method in a second parameter list: +(`RightProj`) containing a `Right` (`Left`) if the predicate is +`false`, and where the contents of that `Right` (`Left`) is obtained +using an implicit conversion passed to the `withFilter` method in a +second parameter list: def withFilter[BB >: B](p: A => Boolean) (implicit aToB: Right.Convert => BB): LeftProj[A, BB] = { @@ -147,9 +147,9 @@ when `if` or pattern-matching features in a `for` comprehension: implicit def f(convert: Right.Convert) = convert.any.toString Note that `Convert` is a simple case class which serves to ensure that -the implicit conversion is properly targetted. +the implicit conversion is properly targeted. -This third solution has been demonstrated to work well, and is the one +This third solution has been [demonstrated][project] to work well, and is the one proposed here. ## Part 2: Simplifying use in for-comprehensions by *adding* right-biased capability ## @@ -183,11 +183,19 @@ Finally, note that Part 2 does not render Part 1 redundant; although appropriate in `for` comprehensions involving the `RightProjection`, but not the `LeftProjection`. +## Trial version ## + +A trial verson of `Either` incorporating the proposed enhancements, +and complete with test suites, is maintained [here][project]. + [enhance]: http://robsscala.blogspot.co.uk/2012/04/validation-without-scalaz.html [fix]: http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html + [vs]: + http://robsscala.blogspot.co.uk/2012/06/fixing-scalaeither-unbiased-vs-biased.html [debate1]: https://groups.google.com/group/scala-debate/browse_thread/thread/2bac2fe8aa6124ad?hl=en [debate2]: https://groups.google.com/forum/?fromgroups#!topic/scala-debate/XlN-oqbslS0 [report]: https://issues.scala-lang.org/browse/SI-5793 + [project]: https://github.com/robcd/scala-either-proj-map-returns-proj/tree/add_right-bias_2-10_withFilter From 4a003f3abfc9fb5e7a78c2d70c59ce47856f190d Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Fri, 29 Jun 2012 13:41:50 +0100 Subject: [PATCH 08/16] !sips/pending/_posts/2012-06-29}-fixing-either.md: few changes to opening paragraph --- sips/pending/_posts/2012-06-29-fixing-either.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sips/pending/_posts/2012-06-29-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md index e71b79ee9e..a2eb3ef11d 100644 --- a/sips/pending/_posts/2012-06-29-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -6,13 +6,13 @@ title: SIP-20 Fixing Either **By: Rob Dickens** -This proposal is the sole initiative of the author, based on his -attempts, [first][enhance] to understand why `scala.Either` is not -more widely used, and to enhance it, and then ([here][fix] and -[here][vs]) to try to fix it. It is also being made in the light of -two mailing-list debates which have taken place - one (initiated by -Jason Zaugg) about [right-biasing Either][debate1], which was -inconclusive, and a subsequent one about [fixing Either][debate2]. +This proposal is based on the author's attempts, [first][enhance] to +understand why `scala.Either` is not more widely used, and to enhance +it, and then ([here][fix] and [here][vs]) to try to fix it. It owes +much to two mailing-list debates which have taken +place - one (initiated by Jason Zaugg) about [right-biasing +Either][debate1], which was inconclusive, and a subsequent one about +[fixing Either][debate2]. ## Motivation ## From c5f55a3e2e6f658a175a448d9f594c8630a17245 Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Fri, 29 Jun 2012 13:54:05 +0100 Subject: [PATCH 09/16] !sips/pending/_posts/2012-06-29}-fixing-either.md: couple of minor font fixes --- sips/pending/_posts/2012-06-29-fixing-either.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sips/pending/_posts/2012-06-29-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md index a2eb3ef11d..098e03491c 100644 --- a/sips/pending/_posts/2012-06-29-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -107,10 +107,10 @@ becomes } Note that `.e` must be appended to the value the `for` comprehension then -yields, in order to obtain the corresponding `Either` value. +`yield`s, in order to obtain the corresponding `Either` value. Regarding the second example of odd behaviour, involving `if`, this was -traced to the fact that the filter method (of `LeftProjection` and +traced to the fact that the `filter` method (of `LeftProjection` and `RightProjection`) returns an `Option` instead of the respective projection. From b3d186baf809b967fac21c035f2c69d026b41301 Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Fri, 29 Jun 2012 16:29:07 +0100 Subject: [PATCH 10/16] !sips/pending/_posts/2012-06-29-fixing-either.md: one word changed -This third solution has been [demonstrated][project] to work well, and is the one +This third solution has been [shown][project] to work well, and is the one --- sips/pending/_posts/2012-06-29-fixing-either.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sips/pending/_posts/2012-06-29-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md index 098e03491c..18977aefc8 100644 --- a/sips/pending/_posts/2012-06-29-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -149,7 +149,7 @@ when `if` or pattern-matching features in a `for` comprehension: Note that `Convert` is a simple case class which serves to ensure that the implicit conversion is properly targeted. -This third solution has been [demonstrated][project] to work well, and is the one +This third solution has been [shown][project] to work well, and is the one proposed here. ## Part 2: Simplifying use in for-comprehensions by *adding* right-biased capability ## From 6dfb745fad49278cb824216165ef5d04571beb75 Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Fri, 29 Jun 2012 17:43:26 +0100 Subject: [PATCH 11/16] !sips/pending/_posts/2012-06-29-fixing-either.md: another word change -A trial verson of `Either` incorporating the proposed enhancements, +A trial verson of `Either` incorporating the proposed fixes, --- sips/pending/_posts/2012-06-29-fixing-either.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sips/pending/_posts/2012-06-29-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md index 18977aefc8..d941ad5445 100644 --- a/sips/pending/_posts/2012-06-29-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -185,7 +185,7 @@ but not the `LeftProjection`. ## Trial version ## -A trial verson of `Either` incorporating the proposed enhancements, +A trial verson of `Either` incorporating the proposed fixes, and complete with test suites, is maintained [here][project]. [enhance]: http://robsscala.blogspot.co.uk/2012/04/validation-without-scalaz.html From df6434310de813f0fc11b85e5d32be2fda9926fb Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Fri, 29 Jun 2012 21:06:45 +0100 Subject: [PATCH 12/16] !sips/pending/_posts/2012-06-29-fixing-either.md: in Part 1, discussion 'Regarding the second example of odd behaviour' corrected Regarding the second example of odd behaviour, involving `if`, this was traced to the fact that the `filter` method (of `LeftProjection` and `RightProjection`) returns an `Option` instead of the - respective projection. + an `Either`, thus allowing `None` to be returned when the predicate is `false`. A +`Left` (`Right`) could not be returned in the case of a +`RightProjection` (`LeftProjection`) since no value is available to go +into it. --- sips/pending/_posts/2012-06-29-fixing-either.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sips/pending/_posts/2012-06-29-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md index d941ad5445..33b9e08e17 100644 --- a/sips/pending/_posts/2012-06-29-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -109,10 +109,13 @@ becomes Note that `.e` must be appended to the value the `for` comprehension then `yield`s, in order to obtain the corresponding `Either` value. -Regarding the second example of odd behaviour, involving `if`, this was -traced to the fact that the `filter` method (of `LeftProjection` and -`RightProjection`) returns an `Option` instead of the respective -projection. +Regarding the second example of odd behaviour, involving `if`, this +was traced to the fact that the `filter` method (of `LeftProjection` +and `RightProjection`) returns an `Option` instead of an `Either`, +thus allowing `None` to be returned when the predicate is `false`. A +`Left` (`Right`) could not be returned in the case of a +`RightProjection` (`LeftProjection`) since no value is available to go +into it. The first solution considered was to introduce a third subtype, equivalent to `Option`'s `None`, but this was later rejected on the From 25d9db01dac883018d9d0d67be9881ecb3f499b1 Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Sat, 30 Jun 2012 11:05:25 +0100 Subject: [PATCH 13/16] !sips/pending/_posts/2012-06-29-fixing-either.md: +## Migration strategy ## as suggested by Daniel Sobral --- sips/pending/_posts/2012-06-29-fixing-either.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sips/pending/_posts/2012-06-29-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md index 33b9e08e17..d9bc26389b 100644 --- a/sips/pending/_posts/2012-06-29-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -191,6 +191,15 @@ but not the `LeftProjection`. A trial verson of `Either` incorporating the proposed fixes, and complete with test suites, is maintained [here][project]. +## Migration strategy ## + +As mentioned in Part 1, `LeftProjection`, `RightProjection`, and +therefore also `Either`'s `left` and `right` methods, should be deprecated, +as has been done in the trial version. + +All other changes involve adding new methods to `Either`, which is +unlikely to break existing code. + [enhance]: http://robsscala.blogspot.co.uk/2012/04/validation-without-scalaz.html [fix]: http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html From 945389c60673271706d6d0f9b5a5bae0ca00d91f Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Mon, 2 Jul 2012 12:17:34 +0100 Subject: [PATCH 14/16] !sips/pending/_posts/2012-06-29-fixing-either.md: 'aToB: Right.Convert => BB' explanation added; reference to Tony's filter objection added --- .../_posts/2012-06-29-fixing-either.md | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/sips/pending/_posts/2012-06-29-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md index d9bc26389b..4d62d3e69a 100644 --- a/sips/pending/_posts/2012-06-29-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -143,16 +143,34 @@ second parameter list: LeftProj(e2) } +Note that `aToB` has the type, `Right.Convert => BB`, rather than `A => +BB`, as might have been expected. This deserves the following explanation: + +* `A => BB` only permits `for` comprehensions that do not feature definitions +* `Tuple2[...] => BB` is required if there is 1 definition +* `Tuple3[...] => BB` is required if there are 2 definitions, and so on +* the tuple comprises one field for each definition, plus a further one (the first) for `a` +* `Any` is a suitable catch-all, that must be placed in a suitable + container, in order that implicit look-ups should be properly targeted +* `Right.Convert` is such a container (for an `a` or tuple that is a + 'convert' to the right, and must therefore be converted to a `BB` + by an implicit conversion) + This solution therefore requires that an implicit conversion such as the following be provided whenever the method is used, as is the case when `if` or pattern-matching features in a `for` comprehension: implicit def f(convert: Right.Convert) = convert.any.toString -Note that `Convert` is a simple case class which serves to ensure that -the implicit conversion is properly targeted. +Here, `any` is an `a` or tuple that is a convert to the right, and `BB` is `String`. + +Although this solution has been [shown][project] to work well, +it has been objected to on the grounds that [`Either` strictly requires +additional support][filterObjection] in order that it may have a filter. -This third solution has been [shown][project] to work well, and is the one +However, considering that that necessary support is apparently +unlikely to be added to the Scala library, together with the +implications of not providing a filter, this third solution is the one proposed here. ## Part 2: Simplifying use in for-comprehensions by *adding* right-biased capability ## @@ -211,3 +229,4 @@ unlikely to break existing code. https://groups.google.com/forum/?fromgroups#!topic/scala-debate/XlN-oqbslS0 [report]: https://issues.scala-lang.org/browse/SI-5793 [project]: https://github.com/robcd/scala-either-proj-map-returns-proj/tree/add_right-bias_2-10_withFilter + [filterObjection]: https://groups.google.com/d/msg/scala-debate/XlN-oqbslS0/C-K0GjvbfowJ From 0abaa044600d8a216b7c429f2cd77f6a09ffed45 Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Wed, 4 Jul 2012 13:14:11 +0100 Subject: [PATCH 15/16] !sips/pending/_posts/2012-06-29-fixing-either.md: rewrote discussion around withFilter, now that Convert retains the type of its field --- .../_posts/2012-06-29-fixing-either.md | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/sips/pending/_posts/2012-06-29-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md index 4d62d3e69a..2b90d0381d 100644 --- a/sips/pending/_posts/2012-06-29-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -112,7 +112,7 @@ Note that `.e` must be appended to the value the `for` comprehension then Regarding the second example of odd behaviour, involving `if`, this was traced to the fact that the `filter` method (of `LeftProjection` and `RightProjection`) returns an `Option` instead of an `Either`, -thus allowing `None` to be returned when the predicate is `false`. A +thus allowing `None` to be returned when the predicate is false. A `Left` (`Right`) could not be returned in the case of a `RightProjection` (`LeftProjection`) since no value is available to go into it. @@ -130,12 +130,12 @@ pattern-matching in `for` comprehensions involving (projections of) Therefore, a third solution has been investigated, whereby `LeftProj` (`RightProj`) has a `withFilter` method that returns a `LeftProj` (`RightProj`) containing a `Right` (`Left`) if the predicate is -`false`, and where the contents of that `Right` (`Left`) is obtained +false, and where the contents of that `Right` (`Left`) is obtained using an implicit conversion passed to the `withFilter` method in a second parameter list: def withFilter[BB >: B](p: A => Boolean) - (implicit aToB: Right.Convert => BB): LeftProj[A, BB] = { + (implicit aToB: Right.Convert[A] => BB): LeftProj[A, BB] = { val e2: Either[A, BB] = e match { case Left(a) => if (p(a)) Left(a) else Right(aToB(Right.Convert(a))) case Right(b) => Right(b) @@ -143,26 +143,46 @@ second parameter list: LeftProj(e2) } -Note that `aToB` has the type, `Right.Convert => BB`, rather than `A => -BB`, as might have been expected. This deserves the following explanation: +Note that `aToB` has the type, `Right.Convert[A] => BB`, rather than +`A => BB`, since we're obliged to wrap the `a` in something whose type +is specific to this type of conversion--from a value in a `Left` to +a value that can go into a `Right`--in order that a targeted implicit +conversion may be supplied. `Convert` is a simple case class, -* `A => BB` only permits `for` comprehensions that do not feature definitions -* `Tuple2[...] => BB` is required if there is 1 definition -* `Tuple3[...] => BB` is required if there are 2 definitions, and so on -* the tuple comprises one field for each definition, plus a further one (the first) for `a` -* `Any` is a suitable catch-all, that must be placed in a suitable - container, in order that implicit look-ups should be properly targeted -* `Right.Convert` is such a container (for an `a` or tuple that is a - 'convert' to the right, and must therefore be converted to a `BB` - by an implicit conversion) + case class Convert[+A](a: A) -This solution therefore requires that an implicit conversion such as -the following be provided whenever the method is used, as is the case -when `if` or pattern-matching features in a `for` comprehension: +with a `Left` counterpart for use in conversions going the other +way. - implicit def f(convert: Right.Convert) = convert.any.toString +It should be noted that `a` is sometimes a tuple: -Here, `any` is an `a` or tuple that is a convert to the right, and `BB` is `String`. +* in `for` comprehensions involving an `if` referring to a definition, + the compiler demands that an implicit conversion from a `Tuple2[A, X]` + be provided, where `X` is the type of the definition, instead of a + conversion from an `A` + +* if there are two definitions, this will be a `Tuple3[A, X, Y]`, where + `Y` is the type of the second definition, and so on + +* at runtime, if the expression following `if` is false, `withFilter` + is called as though the type of the `LeftProj` were + `LeftProj[TupleN[A, ...], BB]` (instead of `LeftProj[A, BB]`). + +For example, given the following `for` comprehension, + + val either: Either[String, Int] = Right(1) + val res = for { + a <- either.rp + b = a + 1 + if b > 0 + } yield b + assert(res.e == Right(2)) + +the compiler would demand a conversion such as the following, + + implicit def g(convert: Left.Convert[(Int, Int)]) = convert.b.toString + +so that `res.e` would be `Left("(1,2)")` if `b > 0` were false. Although this solution has been [shown][project] to work well, it has been objected to on the grounds that [`Either` strictly requires From 7abf16b1faa0829d212f50dd54bb8b47103fe8ed Mon Sep 17 00:00:00 2001 From: Rob Dickens Date: Wed, 4 Jul 2012 13:46:19 +0100 Subject: [PATCH 16/16] !sips/pending/_posts/2012-06-29-fixing-either.md: withFilter discussion now mentions the possibility of using polymorphic implicit conversions -so that `res.e` would be `Left("(1,2)")` if `b > 0` were false. +so that `res.e` would be `Left("(1,2)")` if `b > 0` were false. Note +that `g` may be given a type parameter, in case multiple conversions +would otherwise be needed, and a single, common, implementation would suffice. --- sips/pending/_posts/2012-06-29-fixing-either.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sips/pending/_posts/2012-06-29-fixing-either.md b/sips/pending/_posts/2012-06-29-fixing-either.md index 2b90d0381d..7327b4dbd5 100644 --- a/sips/pending/_posts/2012-06-29-fixing-either.md +++ b/sips/pending/_posts/2012-06-29-fixing-either.md @@ -182,7 +182,9 @@ the compiler would demand a conversion such as the following, implicit def g(convert: Left.Convert[(Int, Int)]) = convert.b.toString -so that `res.e` would be `Left("(1,2)")` if `b > 0` were false. +so that `res.e` would be `Left("(1,2)")` if `b > 0` were false. Note +that `g` may be given a type parameter, in case multiple conversions +would otherwise be needed, and a single, common, implementation would suffice. Although this solution has been [shown][project] to work well, it has been objected to on the grounds that [`Either` strictly requires