Monday 28 May 2012

Fixing scala.Either - left/right.map returns projection

[2012-06-06 Update: support for 'if' in for-comprehensions now removed - result must be either Left or Right, and never empty!]
[2012-06-11 Update: added link to 'tests involving Option'.]

That Either can, after all, participate in for comprehensions involving multiple generators was a revelation of an earlier post.

To recap, you need to indicate which of the two possible results you're interested in, by using either the left: LeftProjection or right: RightProjection of the Either. For example, multiple checks (each returning an Either[..., Int]) on some a: Int, would be carried out as follows:


What needs fixing

However, it has subsequently been pointed out (in this bug report), that definitions aren't available,


and worse still, I notice that neither can you use if together with multiple generators and yield:


Debate

The above bug-report resulted in a debate about a proposal to fix Either by modifying it in such a way that it would behave like its right: RightProjection. This so-called right-biasing would therefore eliminate the need to specify which result you were interested in (and involve left and right being deprecated).

However, not everyone agreed with this, with some calling for an alternative class to be introduced.

My own opinion is that Either should just be patched so as to enable the above two cases to work. (I don't agree with the right-biasing proposal, since the class's very name does not imply any bias; I'd sooner see an unbiased Either alongside a biased alternative, with a name such as Checked.)

Patch

So I ended up trying to do the patching myself, and against the odds, now appear to have succeeded.

Rather than have left/right.map return an Either, it now returns a Left-/RightProjection.

This means that yield will result in a Left-/RightProjection instead of an Either, which just requires that you add .e to obtain the Either:


The patched Either is maintained in a Github project called scala-either-proj-map-returns-proj, including for comprehension tests, tests involving Option, and the test app from which the example above was taken.

Feel free to build the project yourself, and add further tests that try to break something.

Comments welcome.

Wednesday 16 May 2012

EitherExtras class files now published

EitherExtras is now in a package called org.lafros.scala, and licensed for use under the Apache v2.0 licence.

A jar file containing Scala 2.9.2-compatible class files is now available in the Maven central repository. To make use of it, add the following to your build.sbt,
libraryDependencies += "org.lafros" %% "scala-either-extras" % "1.0"
or the following to your pom.xml.
<dependency>
  <groupId>org.lafros</groupId>
  <artifactId>scala-either-extras</artifactId>
  <version>1.0</version>
</dependency>
Please report issues here.

Many thanks to Sonatype for providing their publishing service, and to Josh Suereth et al. for providing instructions.

Monday 7 May 2012

Validating multiple values at once (without Scalaz)

[2012-05-08 Update: now using uncurried case-class constructor in final example.]

This is useful if you want to pass them to a function (and only do so if they're all valid).

The initial version of the EitherExtras type class, introduced in my last post, has now been endowed with the necessary support.

Here are some examples to show how to use it.

The following code, which would rely on methods a and b throwing an exception in the event of failure...


...may be rewritten so as to use scala.Either instead, like so:


Note the following:

  • EitherExtras itself is no longer a type class, and may now be mixed into the self type, as here
  • it has an abstract type, L, to be assigned the type of the contents of the Left result, when validating multiple values at once
  • f is required to be curried
  • fast applies it in a fail-fast manner: if method a returns a Left, that will be the result (and b is not called)
  • conversely, slow would still call b, and return a Left containing a List, possible including the invalid result from that as well
  • <*> was chosen as the operator, for conformity with the Haskell language, since we're now using Either as an applicative functor.
Here are some implementations and corresponding results.


Please see the usingEitherApp to run this code yourself.

The next example shows how the above may be used together with the implicit methods for applying multiple checks to a single value (that EitherExtras provides already).


Note that the two original methods (check, checkAndMap) have now become four (fastCheck, fastCheckAndMap, slowCheck, slowCheckAndMap), so as to allow either fail-fast or fail-slow application of the checks, as described above.

Please see usingEitherApp2 for implementations and results, and to run the example yourself.

The final example is slightly less minimalist and abstract, and involves a case class.


Note how the uncurried case-class constructor is converted to the required curried form. (Alternatively, case classes may be created using a curried constructor, although then, only the first parameter is mentioned by toString.)

For implementations and results, and to run this example yourself, please see usingEitherApp3.

Finally, I should mention that this latest effort was prompted (and the last example inspired) by a comment made by Chris Marshall in response to my previous post.

Again, help yourself to this new version of EitherExtras, which now occupies the master branch of the scala-either-extras project on Github - the original version has been moved to a new branch called simple.

Comments welcome.