FAQ
I'm still puzzling and struggling over how to present exception-handling
shortcuts (Try, catching, handling) to beginners. One thing I've just now
discovered is that *Success* and *Failure* *do* work inside *for*
comprehensions:

// TryMore.scala
import util.{Try, Success, Failure}

def A(b:Boolean) =
if(b) Success(1)
else Failure(new Exception("Ex A"))

def B(b:Boolean) =
if(b) Success(2)
else Failure(new Exception("Ex B"))

def C(x:Boolean, y:Boolean) = for {
res1 <- A(x)
res2 <- B(y)
} yield res1 + res2

println(C(true, true)) // Success(3)
println(C(true, false)) // Failure(java.lang.Exception: Ex B)
println(C(false, true)) // Failure(java.lang.Exception: Ex A)
println(C(false, false)) // Failure(java.lang.Exception: Ex A)

So now I'm back to wondering why I shouldn't suggest returning *Success* and
*Failure* from functions.

(I suppose this question is primarily for Josh...).

Search Discussions

  • Lanny Ripple at Sep 28, 2012 at 8:09 pm
    It's fine to return Success or Failure from a function. If I recall your
    original post the problem you were first having was ending up with a
    Try[Try[Int]] and not having an easy way to join out the inner Try.
    for/yield is using .flatMap on Try under the covers which does exactly the
    join you need. The for/yield you've written gets translated to

    def C(x: Boolean, y: Boolean): Try[Int] = A(x).flatMap{
    res1 => B(y).map{
    res2 => res1 + res2
    }
    }

    When you want to avoid nesting something like Try (e.g., Try[Try[Int]])
    turn to .flatMap (or for/yield which will do it for you).

    -ljr

    On Friday, September 28, 2012 1:15:21 PM UTC-6, Bruce Eckel wrote:

    I'm still puzzling and struggling over how to present exception-handling
    shortcuts (Try, catching, handling) to beginners. One thing I've just now
    discovered is that *Success* and *Failure* *do* work inside *for*
    comprehensions:

    // TryMore.scala
    import util.{Try, Success, Failure}

    def A(b:Boolean) =
    if(b) Success(1)
    else Failure(new Exception("Ex A"))

    def B(b:Boolean) =
    if(b) Success(2)
    else Failure(new Exception("Ex B"))

    def C(x:Boolean, y:Boolean) = for {
    res1 <- A(x)
    res2 <- B(y)
    } yield res1 + res2

    println(C(true, true)) // Success(3)
    println(C(true, false)) // Failure(java.lang.Exception: Ex B)
    println(C(false, true)) // Failure(java.lang.Exception: Ex A)
    println(C(false, false)) // Failure(java.lang.Exception: Ex A)

    So now I'm back to wondering why I shouldn't suggest returning *Success* and
    *Failure* from functions.

    (I suppose this question is primarily for Josh...).
  • Josh Suereth at Sep 29, 2012 at 1:56 am
    So, try is meant to work in for comprehensions.

    The reason why I recommend having "pure" functions is more about the
    expense of making an exception to represent failure. Try is useful when
    trying to taken exception throwing code and deal with it in a different way.

    However, if you take your normal code, where you don't want to throw
    exceptions, and start using Success/Failure, you've just tied yourself to
    having a stack trace generated for every failure even if it's completely
    unnecessary!

    That's why I'd rather wrap things in Try right in the for rather than
    create a less-flexible pure method.

    Now - if you're sold on using try for all error reporting, then there's no
    harm for the codebase, besides that Try may not be the right fit if you
    need to aggregate errors....
    On Sep 28, 2012 3:15 PM, "Bruce Eckel" wrote:

    I'm still puzzling and struggling over how to present exception-handling
    shortcuts (Try, catching, handling) to beginners. One thing I've just now
    discovered is that *Success* and *Failure* *do* work inside *for*
    comprehensions:

    // TryMore.scala
    import util.{Try, Success, Failure}

    def A(b:Boolean) =
    if(b) Success(1)
    else Failure(new Exception("Ex A"))

    def B(b:Boolean) =
    if(b) Success(2)
    else Failure(new Exception("Ex B"))

    def C(x:Boolean, y:Boolean) = for {
    res1 <- A(x)
    res2 <- B(y)
    } yield res1 + res2

    println(C(true, true)) // Success(3)
    println(C(true, false)) // Failure(java.lang.Exception: Ex B)
    println(C(false, true)) // Failure(java.lang.Exception: Ex A)
    println(C(false, false)) // Failure(java.lang.Exception: Ex A)

    So now I'm back to wondering why I shouldn't suggest returning *Success* and
    *Failure* from functions.

    (I suppose this question is primarily for Josh...).
  • Bruce Eckel at Sep 30, 2012 at 11:32 am
    Josh:

    While I don't mind, for the sake of pedagogy, telling little white (but
    reversible) lies, tying to a stack trace to report a failure feels like it
    crosses the line into bad practice.

    It would be nice if (and I don't know if this is possible):
    1) When you call Try it does what it does now.
    2) There is an "overloaded" version of Failure that, when used as an
    ordinary return value, just carries aggregate failure data rather than
    exception objects.
    Then Success and Failure could be used in *for *comprehensions etc. as
    before, regardless of whether they come from a call to Try or a call to a
    method that returns Success/Failure for error reporting.

    Although I like the simplified error reporting approach you suggested
    (leaving off the "sealed" because we're not introducing that in the book;
    it doesn't seem to hurt too much to add it later -- doesn't require clients
    to rewrite code):
    *trait Result
    case class OK(x:Int, y:String) extends Result
    case class Failed(err: Exception) extends Result*

    It's straightforward to introduce, but the results can't be used in
    *for*comprehensions etc. without adding flatMap and whatever else is
    required to
    make it a monad (I think?), which are beyond the scope of the book.

    I *might* be able to figure out how to make some kind of generic
    Result/OK/Failed by copying what's in Option and Success/Failure so that it
    will work in *for *comprehensions etc., and put it in an appendix
    indicating that the details are beyond the book's scope. Then it would be a
    nicer and more useful error reporting mechanism. Obviously it would be
    preferable to have the overloaded Failure in the standard library instead.

    Or I could just introduce the simplified error reporting, as a concept and
    useful basic tool, but say that there's a better way that's beyond the
    book, and that we hope might be introduced into a later version of the
    Scala distribution.

    Thoughts?

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Fri, Sep 28, 2012 at 7:55 PM, Josh Suereth wrote:

    So, try is meant to work in for comprehensions.

    The reason why I recommend having "pure" functions is more about the
    expense of making an exception to represent failure. Try is useful when
    trying to taken exception throwing code and deal with it in a different way.

    However, if you take your normal code, where you don't want to throw
    exceptions, and start using Success/Failure, you've just tied yourself to
    having a stack trace generated for every failure even if it's completely
    unnecessary!

    That's why I'd rather wrap things in Try right in the for rather than
    create a less-flexible pure method.

    Now - if you're sold on using try for all error reporting, then there's no
    harm for the codebase, besides that Try may not be the right fit if you
    need to aggregate errors....
    On Sep 28, 2012 3:15 PM, "Bruce Eckel" wrote:

    I'm still puzzling and struggling over how to present exception-handling
    shortcuts (Try, catching, handling) to beginners. One thing I've just now
    discovered is that *Success* and *Failure* *do* work inside *for*
    comprehensions:

    // TryMore.scala
    import util.{Try, Success, Failure}

    def A(b:Boolean) =
    if(b) Success(1)
    else Failure(new Exception("Ex A"))

    def B(b:Boolean) =
    if(b) Success(2)
    else Failure(new Exception("Ex B"))

    def C(x:Boolean, y:Boolean) = for {
    res1 <- A(x)
    res2 <- B(y)
    } yield res1 + res2

    println(C(true, true)) // Success(3)
    println(C(true, false)) // Failure(java.lang.Exception: Ex B)
    println(C(false, true)) // Failure(java.lang.Exception: Ex A)
    println(C(false, false)) // Failure(java.lang.Exception: Ex A)

    So now I'm back to wondering why I shouldn't suggest returning *Success* and
    *Failure* from functions.

    (I suppose this question is primarily for Josh...).
  • Erik Post at Sep 30, 2012 at 1:06 pm

    On Sunday, 30 September 2012 13:32:18 UTC+2, Bruce Eckel wrote:
    Josh:

    While I don't mind, for the sake of pedagogy, telling little white (but
    reversible) lies, tying to a stack trace to report a failure feels like it
    crosses the line into bad practice.

    It would be nice if (and I don't know if this is possible):
    1) When you call Try it does what it does now.
    2) There is an "overloaded" version of Failure that, when used as an
    ordinary return value, just carries aggregate failure data rather than
    exception objects.
    Then Success and Failure could be used in *for *comprehensions etc. as
    before, regardless of whether they come from a call to Try or a call to a
    method that returns Success/Failure for error reporting.
    Hm, I'm with you in not understanding why stack traces should be needed for
    *every* Failure. Lift has the Box type, which is a bit like Try, I suppose.
    [1] The fact that Box's exception or nested chain of failures is optional
    makes it cheap to use anywhere you like. Stack traces are unnecessary in a
    lot of cases where I return a Failure, and I'm surprised to read that Try
    apparently isn't going to provide this option. Is the idea to use Either or
    something similar instead? I'd find that quite confusing to explain to
    newcomers, and I imagine mixing Try and Either might not be without its own
    set of problems.

    Cheers,
    Erik


    [1] From net.liftweb.common.Box:

    **
    * A Failure is an EmptyBox with an additional failure message explaining
    the reason for its being empty.
    * It can also optionally provide an exception or a chain of causes
    represented as a list of other Failure objects
    */
    sealed case class Failure(msg: String, exception: Box[Throwable], chain:
    Box[Failure]) extends EmptyBox
  • Roland Kuhn at Sep 30, 2012 at 3:28 pm
    Hi Bruce & Erik,

    it has been said several times already, but allow me to repeat: Try is not about failure handling, it is about exception handling. Exceptions (very typically) are failures, but the other direction is not true. For failure handling there are better tools available, which OTOH do not solve the exception handling part.

    So: Try vs Validation are as different as “exceptions” vs “failures”.

    Regards,

    Roland

    30 sep 2012 kl. 06:06 skrev Erik Post:
    On Sunday, 30 September 2012 13:32:18 UTC+2, Bruce Eckel wrote:
    Josh:

    While I don't mind, for the sake of pedagogy, telling little white (but reversible) lies, tying to a stack trace to report a failure feels like it crosses the line into bad practice.

    It would be nice if (and I don't know if this is possible):
    1) When you call Try it does what it does now.
    2) There is an "overloaded" version of Failure that, when used as an ordinary return value, just carries aggregate failure data rather than exception objects.
    Then Success and Failure could be used in for comprehensions etc. as before, regardless of whether they come from a call to Try or a call to a method that returns Success/Failure for error reporting.

    Hm, I'm with you in not understanding why stack traces should be needed for *every* Failure. Lift has the Box type, which is a bit like Try, I suppose. [1] The fact that Box's exception or nested chain of failures is optional makes it cheap to use anywhere you like. Stack traces are unnecessary in a lot of cases where I return a Failure, and I'm surprised to read that Try apparently isn't going to provide this option. Is the idea to use Either or something similar instead? I'd find that quite confusing to explain to newcomers, and I imagine mixing Try and Either might not be without its own set of problems.

    Cheers,
    Erik


    [1] From net.liftweb.common.Box:

    **
    * A Failure is an EmptyBox with an additional failure message explaining the reason for its being empty.
    * It can also optionally provide an exception or a chain of causes represented as a list of other Failure objects
    */
    sealed case class Failure(msg: String, exception: Box[Throwable], chain: Box[Failure]) extends EmptyBox
    Roland Kuhn
    Typesafe – The software stack for applications that scale.
    twitter: @rolandkuhn
  • Bruce Eckel at Sep 30, 2012 at 4:15 pm
    But if it's possible to solve a more general problem, why arbitrarily
    narrow the application of Success and Failure to something more specific?

    Error handling is already messy and confusing enough; Either, for example,
    is terrifically counterintuitive as an error-reporting mechanism.

    Error reporting and handling is a fundamental part of programming, and I
    think we should be trying to unify the error system in Scala rather than
    encouraging more and more bifurcated approaches. That's what happened in C:
    a smattering of approaches that no one ended up using. Yes, returning a
    disjoint union in Scala forces client programmers to pay attention but
    that's not enough. I should be able to mix the results produced by Try with
    the results from non-exceptional failures inside a *for* comprehension.

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Sun, Sep 30, 2012 at 9:27 AM, Roland Kuhn wrote:

    Hi Bruce & Erik,

    it has been said several times already, but allow me to repeat: Try is not
    about failure handling, it is about exception handling. Exceptions (very
    typically) are failures, but the other direction is not true. For failure
    handling there are better tools available, which OTOH do not solve the
    exception handling part.

    So: Try vs Validation are as different as “exceptions” vs “failures”.

    Regards,

    Roland

    30 sep 2012 kl. 06:06 skrev Erik Post:
    On Sunday, 30 September 2012 13:32:18 UTC+2, Bruce Eckel wrote:

    Josh:

    While I don't mind, for the sake of pedagogy, telling little white (but
    reversible) lies, tying to a stack trace to report a failure feels like it
    crosses the line into bad practice.

    It would be nice if (and I don't know if this is possible):
    1) When you call Try it does what it does now.
    2) There is an "overloaded" version of Failure that, when used as an
    ordinary return value, just carries aggregate failure data rather than
    exception objects.
    Then Success and Failure could be used in *for *comprehensions etc. as
    before, regardless of whether they come from a call to Try or a call to a
    method that returns Success/Failure for error reporting.
    Hm, I'm with you in not understanding why stack traces should be needed
    for *every* Failure. Lift has the Box type, which is a bit like Try, I
    suppose. [1] The fact that Box's exception or nested chain of failures is
    optional makes it cheap to use anywhere you like. Stack traces are
    unnecessary in a lot of cases where I return a Failure, and I'm surprised
    to read that Try apparently isn't going to provide this option. Is the idea
    to use Either or something similar instead? I'd find that quite confusing
    to explain to newcomers, and I imagine mixing Try and Either might not be
    without its own set of problems.

    Cheers,
    Erik


    [1] From net.liftweb.common.Box:

    **
    * A Failure is an EmptyBox with an additional failure message explaining
    the reason for its being empty.
    * It can also optionally provide an exception or a chain of causes
    represented as a list of other Failure objects
    */
    sealed case class Failure(msg: String, exception: Box[Throwable], chain:
    Box[Failure]) extends EmptyBox


    Roland Kuhn
    Typesafe <http://typesafe.com/> – The software stack for applications
    that scale.
    twitter: @rolandkuhn <http://twitter.com/#!/rolandkuhn>
  • √iktor Ҡlang at Sep 30, 2012 at 6:11 pm

    On Sun, Sep 30, 2012 at 6:15 PM, Bruce Eckel wrote:

    But if it's possible to solve a more general problem, why arbitrarily
    narrow the application of Success and Failure to something more specific?

    That's like saying you should be able to throw whatever you want, and not
    only Throwables.

    Error handling is already messy and confusing enough; Either, for example,
    is terrifically counterintuitive as an error-reporting mechanism.
    Either is not an error handling construct, it's a disjoint union.

    Error reporting and handling is a fundamental part of programming, and I
    think we should be trying to unify the error system in Scala rather than
    encouraging more and more bifurcated approaches. That's what happened in C:
    a smattering of approaches that no one ended up using. Yes, returning a
    disjoint union in Scala forces client programmers to pay attention but
    that's not enough. I should be able to mix the results produced by Try with
    the results from non-exceptional failures inside a *for* comprehension.
    You can do exactly that. Can you show me code that demonstrates your point?

    Thanks,




    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Sun, Sep 30, 2012 at 9:27 AM, Roland Kuhn wrote:

    Hi Bruce & Erik,

    it has been said several times already, but allow me to repeat: Try is
    not about failure handling, it is about exception handling. Exceptions
    (very typically) are failures, but the other direction is not true. For
    failure handling there are better tools available, which OTOH do not solve
    the exception handling part.

    So: Try vs Validation are as different as “exceptions” vs “failures”.

    Regards,

    Roland

    30 sep 2012 kl. 06:06 skrev Erik Post:
    On Sunday, 30 September 2012 13:32:18 UTC+2, Bruce Eckel wrote:

    Josh:

    While I don't mind, for the sake of pedagogy, telling little white (but
    reversible) lies, tying to a stack trace to report a failure feels like it
    crosses the line into bad practice.

    It would be nice if (and I don't know if this is possible):
    1) When you call Try it does what it does now.
    2) There is an "overloaded" version of Failure that, when used as an
    ordinary return value, just carries aggregate failure data rather than
    exception objects.
    Then Success and Failure could be used in *for *comprehensions etc. as
    before, regardless of whether they come from a call to Try or a call to a
    method that returns Success/Failure for error reporting.
    Hm, I'm with you in not understanding why stack traces should be needed
    for *every* Failure. Lift has the Box type, which is a bit like Try, I
    suppose. [1] The fact that Box's exception or nested chain of failures is
    optional makes it cheap to use anywhere you like. Stack traces are
    unnecessary in a lot of cases where I return a Failure, and I'm surprised
    to read that Try apparently isn't going to provide this option. Is the idea
    to use Either or something similar instead? I'd find that quite confusing
    to explain to newcomers, and I imagine mixing Try and Either might not be
    without its own set of problems.

    Cheers,
    Erik


    [1] From net.liftweb.common.Box:

    **
    * A Failure is an EmptyBox with an additional failure message explaining
    the reason for its being empty.
    * It can also optionally provide an exception or a chain of causes
    represented as a list of other Failure objects
    */
    sealed case class Failure(msg: String, exception: Box[Throwable], chain:
    Box[Failure]) extends EmptyBox


    Roland Kuhn
    Typesafe <http://typesafe.com/> – The software stack for applications
    that scale.
    twitter: @rolandkuhn <http://twitter.com/#!/rolandkuhn>

    --
    Viktor Klang

    Akka Tech Lead
    Typesafe <http://www.typesafe.com/> - The software stack for applications
    that scale

    Twitter: @viktorklang
  • Roland Kuhn at Sep 30, 2012 at 6:38 pm
    Hi Bruce,

    30 sep 2012 kl. 09:15 skrev Bruce Eckel:
    But if it's possible to solve a more general problem, why arbitrarily narrow the application of Success and Failure to something more specific?
    I think the analogy here is the different collections implementing the Seq interface: why have subclasses with different performance characteristics for the different operations?

    Maybe your point is really about the choice of naming: as it stands, Try is a specific leaf node (see more below) in the failure handling concept hierarchy. Validation is a different leaf node here, Either might be regarded as another one, and you are after their parent. The problem with unifying e.g. Try and Validation is that Try is fail-fast while Validation tries all operations and aggregates the failures. Having this big semantic difference for different implementations of the same operation is not a good idea in my opinion.
    Error handling is already messy and confusing enough; Either, for example, is terrifically counterintuitive as an error-reporting mechanism.
    My understanding is that that horse is quite dead ;-)
    Error reporting and handling is a fundamental part of programming, and I think we should be trying to unify the error system in Scala rather than encouraging more and more bifurcated approaches. That's what happened in C: a smattering of approaches that no one ended up using. Yes, returning a disjoint union in Scala forces client programmers to pay attention but that's not enough. I should be able to mix the results produced by Try with the results from non-exceptional failures inside a for comprehension.
    You sure can lift a Try into a Validation, in that direction there is no problem.

    To expand a bit on the argument for keeping the current Try as it is: exceptions are used on the JVM to communicate failures to the caller of a method. In the context of Futures, the caller is not available on the current stack of execution, which is the reason why Try was introduced—to reify the bottom type and be able to pass it across thread boundaries to the calling context. This very specific problem needs a good solution, because catching exceptions is something which can easily be done wrong generically. And because not only Futures benefit from decoupling the logical and physical calling contexts for exceptions, Try is not declared within the “object Future” but available for general consumption.

    Regards,

    Roland
    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com



    On Sun, Sep 30, 2012 at 9:27 AM, Roland Kuhn wrote:
    Hi Bruce & Erik,

    it has been said several times already, but allow me to repeat: Try is not about failure handling, it is about exception handling. Exceptions (very typically) are failures, but the other direction is not true. For failure handling there are better tools available, which OTOH do not solve the exception handling part.

    So: Try vs Validation are as different as “exceptions” vs “failures”.

    Regards,

    Roland

    30 sep 2012 kl. 06:06 skrev Erik Post:
    On Sunday, 30 September 2012 13:32:18 UTC+2, Bruce Eckel wrote:
    Josh:

    While I don't mind, for the sake of pedagogy, telling little white (but reversible) lies, tying to a stack trace to report a failure feels like it crosses the line into bad practice.

    It would be nice if (and I don't know if this is possible):
    1) When you call Try it does what it does now.
    2) There is an "overloaded" version of Failure that, when used as an ordinary return value, just carries aggregate failure data rather than exception objects.
    Then Success and Failure could be used in for comprehensions etc. as before, regardless of whether they come from a call to Try or a call to a method that returns Success/Failure for error reporting.

    Hm, I'm with you in not understanding why stack traces should be needed for *every* Failure. Lift has the Box type, which is a bit like Try, I suppose. [1] The fact that Box's exception or nested chain of failures is optional makes it cheap to use anywhere you like. Stack traces are unnecessary in a lot of cases where I return a Failure, and I'm surprised to read that Try apparently isn't going to provide this option. Is the idea to use Either or something similar instead? I'd find that quite confusing to explain to newcomers, and I imagine mixing Try and Either might not be without its own set of problems.

    Cheers,
    Erik


    [1] From net.liftweb.common.Box:

    **
    * A Failure is an EmptyBox with an additional failure message explaining the reason for its being empty.
    * It can also optionally provide an exception or a chain of causes represented as a list of other Failure objects
    */
    sealed case class Failure(msg: String, exception: Box[Throwable], chain: Box[Failure]) extends EmptyBox
    Roland Kuhn
    Typesafe – The software stack for applications that scale.
    twitter: @rolandkuhn

    Roland Kuhn
    Typesafe – The software stack for applications that scale.
    twitter: @rolandkuhn
  • Erik Post at Oct 1, 2012 at 12:04 pm
    Hi Roland,

    On Sunday, 30 September 2012 20:39:00 UTC+2, rkuhn wrote:

    Maybe your point is really about the choice of naming: as it stands, Try is
    a specific leaf node (see more below) in the failure handling concept
    hierarchy. Validation is a different leaf node here, Either might be
    regarded as another one, and you are after their parent. The problem with
    unifying e.g. Try and Validation is that Try is fail-fast while Validation
    tries all operations and aggregates the failures.
    So if we restrict ourselves to the Scala standard library for a moment, our
    options are basically Try and Either, right? And we can mix them up like so:

    import scala.util.Try
    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    Wouldn't it be good then to spend a couple more words on this duo than
    Try's scaladoc currently does? The only mention of Either is the slightly
    nebulous: "It's similar to, but semantically different from the
    scala.util.Either<http://www.scala-lang.org/api/milestone/scala/util/Either.html> type."
    I'd prefer if the API docs for the relevant types helped to give new users
    a quick high-level overview of error handling in Scala so they'd know where
    to turn for further reading.

    Cheers,
    Erik
  • Bruce Eckel at Oct 1, 2012 at 4:48 pm
    Your example doesn't work in 2.10 M7:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:11: error: type mismatch;
    found : scala.util.Try[Int]
    required: scala.util.Either[?,?]
    b <- Try(3)
    ^

    Removing the *.right* produces a possibly more illuminating error:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5)
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:10: error: value flatMap is not a member of
    scala.util.Right[Nothing,Int]
    a <- Right(5)
    ^
    Thus, you can't use Either in the same way you can use Option or Try inside
    a for comprehension: Either has no flatmap and is thus (trying out my
    slowly-acreting monadic knowledge here) not monadic. Which makes it seem
    like an even odder choice for error reporting, and odder still that the
    ScalaDoc for Either says "A common use of Either is as an alternative to
    scala.Option<http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/scala/Option.html>
    for
    dealing with possible missing values." Either seems to have very little of
    the abilities of Option.

    Roland, when you said "My understanding is that that horse is quite dead
    ;-)" (in regards to Either) can you clarify whether that means "we've all
    agreed to use Either for error reporting and have moved on" or "we've all
    agreed that the use of Either for error reporting was a really poor choice
    and are tired of discussing it"? If the latter, shouldn't we be discussing
    its replacement?

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Mon, Oct 1, 2012 at 6:04 AM, Erik Post wrote:

    Hi Roland,

    On Sunday, 30 September 2012 20:39:00 UTC+2, rkuhn wrote:

    Maybe your point is really about the choice of naming: as it stands, Try
    is a specific leaf node (see more below) in the failure handling concept
    hierarchy. Validation is a different leaf node here, Either might be
    regarded as another one, and you are after their parent. The problem with
    unifying e.g. Try and Validation is that Try is fail-fast while Validation
    tries all operations and aggregates the failures.
    So if we restrict ourselves to the Scala standard library for a moment,
    our options are basically Try and Either, right? And we can mix them up
    like so:

    import scala.util.Try
    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    Wouldn't it be good then to spend a couple more words on this duo than
    Try's scaladoc currently does? The only mention of Either is the slightly
    nebulous: "It's similar to, but semantically different from the
    scala.util.Either<http://www.scala-lang.org/api/milestone/scala/util/Either.html> type."
    I'd prefer if the API docs for the relevant types helped to give new users
    a quick high-level overview of error handling in Scala so they'd know where
    to turn for further reading.

    Cheers,
    Erik
  • Erik Post at Oct 1, 2012 at 5:34 pm
    Hi Bruce,
    On Monday, 1 October 2012 18:48:55 UTC+2, Bruce Eckel wrote:

    Your example doesn't work in 2.10 M7:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:11: error: type mismatch;
    found : scala.util.Try[Int]
    required: scala.util.Either[?,?]
    b <- Try(3)
    ^
    Well, I'll be damned... I happened to have tried it in 2.10-M5, and it
    worked like a charm there.


    Removing the *.right* produces a possibly more illuminating error:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5)
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:10: error: value flatMap is not a member of
    scala.util.Right[Nothing,Int]
    a <- Right(5)
    ^
    Thus, you can't use Either in the same way you can use Option or Try
    inside a for comprehension: Either has no flatmap and is thus (trying out
    my slowly-acreting monadic knowledge here) not monadic.
    That's, uhm, right; that's why the .right is there. It returns a
    RightProjection [1] which provides the flatMap that Either itself doesn't
    have. There was a (rather epic) mailing list discussion on right-biased
    Either a while back [2] which I think was decided in favour of no
    right-bias.

    Which makes it seem like an even odder choice for error reporting, and
    odder still that the ScalaDoc for Either says "A common use of Either is
    as an alternative to scala.Option for dealing with possible missing
    values." Either seems to have very little of the abilities of Option.
    I'm not entirely clear on what the Right Thing is here myself. I'm familiar
    with net.liftweb.common.Box and scalaz.Validation, which work nicely for
    me, but I'd dearly like to know what the Scala 2.10 way is.

    Cheers,
    Erik

    [1] http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.util.Either$$RightProjection

    [2] https://groups.google.com/forum/?fromgroups=#!searchin/scala-debate/right$20biased$20either/scala-debate/K6wv6KphJK0/epitkieqt2oJ
  • Daniel Sobral at Oct 1, 2012 at 5:43 pm
    I'd suggest using Try(3).toEither.right, but I've just noticed Try has
    a toOption, but not a toEither. A serious oversight, imho.
    On Mon, Oct 1, 2012 at 1:48 PM, Bruce Eckel wrote:
    Your example doesn't work in 2.10 M7:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:11: error: type mismatch;
    found : scala.util.Try[Int]
    required: scala.util.Either[?,?]
    b <- Try(3)
    ^

    Removing the .right produces a possibly more illuminating error:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5)
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:10: error: value flatMap is not a member of
    scala.util.Right[Nothing,Int]
    a <- Right(5)
    ^
    Thus, you can't use Either in the same way you can use Option or Try inside
    a for comprehension: Either has no flatmap and is thus (trying out my
    slowly-acreting monadic knowledge here) not monadic. Which makes it seem
    like an even odder choice for error reporting, and odder still that the
    ScalaDoc for Either says "A common use of Either is as an alternative to
    scala.Option for dealing with possible missing values." Either seems to have
    very little of the abilities of Option.

    Roland, when you said "My understanding is that that horse is quite dead
    ;-)" (in regards to Either) can you clarify whether that means "we've all
    agreed to use Either for error reporting and have moved on" or "we've all
    agreed that the use of Either for error reporting was a really poor choice
    and are tired of discussing it"? If the latter, shouldn't we be discussing
    its replacement?

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Mon, Oct 1, 2012 at 6:04 AM, Erik Post wrote:

    Hi Roland,
    On Sunday, 30 September 2012 20:39:00 UTC+2, rkuhn wrote:

    Maybe your point is really about the choice of naming: as it stands, Try
    is a specific leaf node (see more below) in the failure handling concept
    hierarchy. Validation is a different leaf node here, Either might be
    regarded as another one, and you are after their parent. The problem with
    unifying e.g. Try and Validation is that Try is fail-fast while Validation
    tries all operations and aggregates the failures.

    So if we restrict ourselves to the Scala standard library for a moment,
    our options are basically Try and Either, right? And we can mix them up like
    so:

    import scala.util.Try
    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    Wouldn't it be good then to spend a couple more words on this duo than
    Try's scaladoc currently does? The only mention of Either is the slightly
    nebulous: "It's similar to, but semantically different from the
    scala.util.Either type." I'd prefer if the API docs for the relevant types
    helped to give new users a quick high-level overview of error handling in
    Scala so they'd know where to turn for further reading.

    Cheers,
    Erik


    --
    Daniel C. Sobral

    I travel to the future all the time.
  • Bruce Eckel at Oct 1, 2012 at 6:07 pm
    I suspect Try has the toOption because that *does* produce something that
    can be used (stepping very hesitatingly as I say this) "monadically"
    whereas Either cannot. Except as you say if you actually force the ...
    projection?

    Again, argh. How come I have to know things about projections and monads
    and all just to understand error reporting? The fundamental problem with
    learning Scala might be that it requires a deep dive into everything in
    order to write something basic.

    Repeat apologies for my frustration. I feel like I've been circling this
    problem for months now.

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Mon, Oct 1, 2012 at 11:34 AM, Daniel Sobral wrote:

    I'd suggest using Try(3).toEither.right, but I've just noticed Try has
    a toOption, but not a toEither. A serious oversight, imho.
    On Mon, Oct 1, 2012 at 1:48 PM, Bruce Eckel wrote:
    Your example doesn't work in 2.10 M7:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:11: error: type mismatch;
    found : scala.util.Try[Int]
    required: scala.util.Either[?,?]
    b <- Try(3)
    ^

    Removing the .right produces a possibly more illuminating error:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5)
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:10: error: value flatMap is not a member of
    scala.util.Right[Nothing,Int]
    a <- Right(5)
    ^
    Thus, you can't use Either in the same way you can use Option or Try inside
    a for comprehension: Either has no flatmap and is thus (trying out my
    slowly-acreting monadic knowledge here) not monadic. Which makes it seem
    like an even odder choice for error reporting, and odder still that the
    ScalaDoc for Either says "A common use of Either is as an alternative to
    scala.Option for dealing with possible missing values." Either seems to have
    very little of the abilities of Option.

    Roland, when you said "My understanding is that that horse is quite dead
    ;-)" (in regards to Either) can you clarify whether that means "we've all
    agreed to use Either for error reporting and have moved on" or "we've all
    agreed that the use of Either for error reporting was a really poor choice
    and are tired of discussing it"? If the latter, shouldn't we be
    discussing
    its replacement?

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Mon, Oct 1, 2012 at 6:04 AM, Erik Post wrote:

    Hi Roland,
    On Sunday, 30 September 2012 20:39:00 UTC+2, rkuhn wrote:

    Maybe your point is really about the choice of naming: as it stands,
    Try
    is a specific leaf node (see more below) in the failure handling
    concept
    hierarchy. Validation is a different leaf node here, Either might be
    regarded as another one, and you are after their parent. The problem
    with
    unifying e.g. Try and Validation is that Try is fail-fast while
    Validation
    tries all operations and aggregates the failures.

    So if we restrict ourselves to the Scala standard library for a moment,
    our options are basically Try and Either, right? And we can mix them up
    like
    so:

    import scala.util.Try
    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    Wouldn't it be good then to spend a couple more words on this duo than
    Try's scaladoc currently does? The only mention of Either is the
    slightly
    nebulous: "It's similar to, but semantically different from the
    scala.util.Either type." I'd prefer if the API docs for the relevant
    types
    helped to give new users a quick high-level overview of error handling
    in
    Scala so they'd know where to turn for further reading.

    Cheers,
    Erik


    --
    Daniel C. Sobral

    I travel to the future all the time.
  • Alec Zorab at Oct 1, 2012 at 7:34 pm
    Compare: "how come I have to learn about classes and static methods and
    voids and string arrays and streams and string literals just to write
    'hello world'"?
    On 1 Oct 2012 19:07, "Bruce Eckel" wrote:

    I suspect Try has the toOption because that *does* produce something that
    can be used (stepping very hesitatingly as I say this) "monadically"
    whereas Either cannot. Except as you say if you actually force the ...
    projection?

    Again, argh. How come I have to know things about projections and monads
    and all just to understand error reporting? The fundamental problem with
    learning Scala might be that it requires a deep dive into everything in
    order to write something basic.

    Repeat apologies for my frustration. I feel like I've been circling this
    problem for months now.

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Mon, Oct 1, 2012 at 11:34 AM, Daniel Sobral wrote:

    I'd suggest using Try(3).toEither.right, but I've just noticed Try has
    a toOption, but not a toEither. A serious oversight, imho.

    On Mon, Oct 1, 2012 at 1:48 PM, Bruce Eckel <bruceteckel@gmail.com>
    wrote:
    Your example doesn't work in 2.10 M7:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:11: error: type mismatch;
    found : scala.util.Try[Int]
    required: scala.util.Either[?,?]
    b <- Try(3)
    ^

    Removing the .right produces a possibly more illuminating error:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5)
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:10: error: value flatMap is not a member of
    scala.util.Right[Nothing,Int]
    a <- Right(5)
    ^
    Thus, you can't use Either in the same way you can use Option or Try inside
    a for comprehension: Either has no flatmap and is thus (trying out my
    slowly-acreting monadic knowledge here) not monadic. Which makes it seem
    like an even odder choice for error reporting, and odder still that the
    ScalaDoc for Either says "A common use of Either is as an alternative to
    scala.Option for dealing with possible missing values." Either seems to have
    very little of the abilities of Option.

    Roland, when you said "My understanding is that that horse is quite dead
    ;-)" (in regards to Either) can you clarify whether that means "we've all
    agreed to use Either for error reporting and have moved on" or "we've all
    agreed that the use of Either for error reporting was a really poor choice
    and are tired of discussing it"? If the latter, shouldn't we be
    discussing
    its replacement?

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Mon, Oct 1, 2012 at 6:04 AM, Erik Post wrote:

    Hi Roland,
    On Sunday, 30 September 2012 20:39:00 UTC+2, rkuhn wrote:

    Maybe your point is really about the choice of naming: as it stands,
    Try
    is a specific leaf node (see more below) in the failure handling
    concept
    hierarchy. Validation is a different leaf node here, Either might be
    regarded as another one, and you are after their parent. The problem
    with
    unifying e.g. Try and Validation is that Try is fail-fast while
    Validation
    tries all operations and aggregates the failures.

    So if we restrict ourselves to the Scala standard library for a moment,
    our options are basically Try and Either, right? And we can mix them
    up like
    so:

    import scala.util.Try
    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    Wouldn't it be good then to spend a couple more words on this duo than
    Try's scaladoc currently does? The only mention of Either is the
    slightly
    nebulous: "It's similar to, but semantically different from the
    scala.util.Either type." I'd prefer if the API docs for the relevant
    types
    helped to give new users a quick high-level overview of error handling
    in
    Scala so they'd know where to turn for further reading.

    Cheers,
    Erik


    --
    Daniel C. Sobral

    I travel to the future all the time.
  • Rex Kerr at Oct 1, 2012 at 8:18 pm
    Error reporting has the illusion of being simple. But, as you're finding
    out, it is anything but. You can either hack together something arbitrary,
    or you have to worry about the generalizations of the concepts you
    require--namely monads and such. In particular, the "magic" that makes for
    work is not magic at all, but adherence to an appropriate general interface.

    Now, it turns out that there are several really desirable properties which
    are mutually incompatible. Either can be right biased--which would make it
    work with for-comprehensions in some ways--but since it has no zero, it
    cannot work with the filtering capability of for (e.g. for (x <- e if x >
    5) yield x+2--this works if e is an option, but cannot if e is a
    right-biased Either). There are also monad laws (that proclaim among other
    things the irrelevancy of order of flatMap operations) that conflict with
    what I call the bulletproof property of error reporting, which is that if
    you have a Try[A], and you do an operation that results in a Try[B], you
    will never throw an exception instead of catching it.

    I think, actually, that opinion was more strongly in favor of right-biasing
    Either than not, which would make it more like Try. But there are still
    two huge differences. One is that Try does have an effective zero
    (actually, a zero-type inhabited by infinitely many values) so it can be
    filtered; also Try can obey the bulletproof property while Either doesn't
    even attempt to catch exceptions.

    So, if you want to teach this without going into all the details, you can
    simply say:
    * If you're worried about throwing exceptions and want to keep track of
    them, use a Try.
    * If you want to keep track of either of two possibilities--maybe an
    error state or a correct result--use Either.
    * You may want to repackage one into the other.

    If you come up with questions like, "Why can't I store a database record in
    Failure?", the answer is, "you can hack it so it does, but that isn't
    designed to be type-safe". If you keep asking "why, why, why", eventually
    you get to the point where you have to worry about monads, zeros, and
    right-vs.-no bias. If you don't keep asking why, you can notice that it
    just works as designed--just like Option can only hold zero or one values,
    not more, and aside from that limitation, it works just fine.

    --Rex
    On Mon, Oct 1, 2012 at 2:07 PM, Bruce Eckel wrote:

    I suspect Try has the toOption because that *does* produce something that
    can be used (stepping very hesitatingly as I say this) "monadically"
    whereas Either cannot. Except as you say if you actually force the ...
    projection?

    Again, argh. How come I have to know things about projections and monads
    and all just to understand error reporting? The fundamental problem with
    learning Scala might be that it requires a deep dive into everything in
    order to write something basic.

    Repeat apologies for my frustration. I feel like I've been circling this
    problem for months now.


    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Mon, Oct 1, 2012 at 11:34 AM, Daniel Sobral wrote:

    I'd suggest using Try(3).toEither.right, but I've just noticed Try has
    a toOption, but not a toEither. A serious oversight, imho.

    On Mon, Oct 1, 2012 at 1:48 PM, Bruce Eckel <bruceteckel@gmail.com>
    wrote:
    Your example doesn't work in 2.10 M7:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:11: error: type mismatch;
    found : scala.util.Try[Int]
    required: scala.util.Either[?,?]
    b <- Try(3)
    ^

    Removing the .right produces a possibly more illuminating error:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5)
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:10: error: value flatMap is not a member of
    scala.util.Right[Nothing,Int]
    a <- Right(5)
    ^
    Thus, you can't use Either in the same way you can use Option or Try inside
    a for comprehension: Either has no flatmap and is thus (trying out my
    slowly-acreting monadic knowledge here) not monadic. Which makes it seem
    like an even odder choice for error reporting, and odder still that the
    ScalaDoc for Either says "A common use of Either is as an alternative to
    scala.Option for dealing with possible missing values." Either seems to have
    very little of the abilities of Option.

    Roland, when you said "My understanding is that that horse is quite dead
    ;-)" (in regards to Either) can you clarify whether that means "we've all
    agreed to use Either for error reporting and have moved on" or "we've all
    agreed that the use of Either for error reporting was a really poor choice
    and are tired of discussing it"? If the latter, shouldn't we be
    discussing
    its replacement?

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Mon, Oct 1, 2012 at 6:04 AM, Erik Post wrote:

    Hi Roland,
    On Sunday, 30 September 2012 20:39:00 UTC+2, rkuhn wrote:

    Maybe your point is really about the choice of naming: as it stands,
    Try
    is a specific leaf node (see more below) in the failure handling
    concept
    hierarchy. Validation is a different leaf node here, Either might be
    regarded as another one, and you are after their parent. The problem
    with
    unifying e.g. Try and Validation is that Try is fail-fast while
    Validation
    tries all operations and aggregates the failures.

    So if we restrict ourselves to the Scala standard library for a moment,
    our options are basically Try and Either, right? And we can mix them
    up like
    so:

    import scala.util.Try
    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    Wouldn't it be good then to spend a couple more words on this duo than
    Try's scaladoc currently does? The only mention of Either is the
    slightly
    nebulous: "It's similar to, but semantically different from the
    scala.util.Either type." I'd prefer if the API docs for the relevant
    types
    helped to give new users a quick high-level overview of error handling
    in
    Scala so they'd know where to turn for further reading.

    Cheers,
    Erik


    --
    Daniel C. Sobral

    I travel to the future all the time.
  • Josh Suereth at Oct 2, 2012 at 2:19 pm
    Just a note: exception handling code is impure and outside the realm of
    the general category we use when we define monads. Try actually fulfills
    the monad laws when you use this category.

    When we talk monads, it's important to understand the category, otherwise
    our talk is meaningless.

    In any case, Validation is not for expression friendly if it allows
    aggregation. You have to use alternative grouping operations before a for,
    like zip for collections.

    I think Rex has a very good point here in general. Error handling has
    different requirements. While I want to see a Validation in the std. Lib,
    it's not there now. Either and Try solve some problems but by no means
    encompass the entire problem.

    I do believe that Validation is great for error handling, and I'd like to
    see such a class exist in the std. To round out the offerings.
    On Oct 1, 2012 4:18 PM, "Rex Kerr" wrote:

    Error reporting has the illusion of being simple. But, as you're finding
    out, it is anything but. You can either hack together something arbitrary,
    or you have to worry about the generalizations of the concepts you
    require--namely monads and such. In particular, the "magic" that makes for
    work is not magic at all, but adherence to an appropriate general interface.

    Now, it turns out that there are several really desirable properties which
    are mutually incompatible. Either can be right biased--which would make it
    work with for-comprehensions in some ways--but since it has no zero, it
    cannot work with the filtering capability of for (e.g. for (x <- e if x >
    5) yield x+2--this works if e is an option, but cannot if e is a
    right-biased Either). There are also monad laws (that proclaim among other
    things the irrelevancy of order of flatMap operations) that conflict with
    what I call the bulletproof property of error reporting, which is that if
    you have a Try[A], and you do an operation that results in a Try[B], you
    will never throw an exception instead of catching it.

    I think, actually, that opinion was more strongly in favor of
    right-biasing Either than not, which would make it more like Try. But
    there are still two huge differences. One is that Try does have an
    effective zero (actually, a zero-type inhabited by infinitely many values)
    so it can be filtered; also Try can obey the bulletproof property while
    Either doesn't even attempt to catch exceptions.

    So, if you want to teach this without going into all the details, you can
    simply say:
    * If you're worried about throwing exceptions and want to keep track of
    them, use a Try.
    * If you want to keep track of either of two possibilities--maybe an
    error state or a correct result--use Either.
    * You may want to repackage one into the other.

    If you come up with questions like, "Why can't I store a database record
    in Failure?", the answer is, "you can hack it so it does, but that isn't
    designed to be type-safe". If you keep asking "why, why, why", eventually
    you get to the point where you have to worry about monads, zeros, and
    right-vs.-no bias. If you don't keep asking why, you can notice that it
    just works as designed--just like Option can only hold zero or one values,
    not more, and aside from that limitation, it works just fine.

    --Rex
    On Mon, Oct 1, 2012 at 2:07 PM, Bruce Eckel wrote:

    I suspect Try has the toOption because that *does* produce something that
    can be used (stepping very hesitatingly as I say this) "monadically"
    whereas Either cannot. Except as you say if you actually force the ...
    projection?

    Again, argh. How come I have to know things about projections and monads
    and all just to understand error reporting? The fundamental problem with
    learning Scala might be that it requires a deep dive into everything in
    order to write something basic.

    Repeat apologies for my frustration. I feel like I've been circling this
    problem for months now.


    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Mon, Oct 1, 2012 at 11:34 AM, Daniel Sobral wrote:

    I'd suggest using Try(3).toEither.right, but I've just noticed Try has
    a toOption, but not a toEither. A serious oversight, imho.

    On Mon, Oct 1, 2012 at 1:48 PM, Bruce Eckel <bruceteckel@gmail.com>
    wrote:
    Your example doesn't work in 2.10 M7:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:11: error: type mismatch;
    found : scala.util.Try[Int]
    required: scala.util.Either[?,?]
    b <- Try(3)
    ^

    Removing the .right produces a possibly more illuminating error:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5)
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:10: error: value flatMap is not a member of
    scala.util.Right[Nothing,Int]
    a <- Right(5)
    ^
    Thus, you can't use Either in the same way you can use Option or Try inside
    a for comprehension: Either has no flatmap and is thus (trying out my
    slowly-acreting monadic knowledge here) not monadic. Which makes it seem
    like an even odder choice for error reporting, and odder still that the
    ScalaDoc for Either says "A common use of Either is as an alternative to
    scala.Option for dealing with possible missing values." Either seems to have
    very little of the abilities of Option.

    Roland, when you said "My understanding is that that horse is quite dead
    ;-)" (in regards to Either) can you clarify whether that means "we've all
    agreed to use Either for error reporting and have moved on" or "we've all
    agreed that the use of Either for error reporting was a really poor choice
    and are tired of discussing it"? If the latter, shouldn't we be
    discussing
    its replacement?

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com



    On Mon, Oct 1, 2012 at 6:04 AM, Erik Post <eriksensei@gmail.com>
    wrote:
    Hi Roland,
    On Sunday, 30 September 2012 20:39:00 UTC+2, rkuhn wrote:

    Maybe your point is really about the choice of naming: as it stands,
    Try
    is a specific leaf node (see more below) in the failure handling
    concept
    hierarchy. Validation is a different leaf node here, Either might be
    regarded as another one, and you are after their parent. The problem
    with
    unifying e.g. Try and Validation is that Try is fail-fast while
    Validation
    tries all operations and aggregates the failures.

    So if we restrict ourselves to the Scala standard library for a
    moment,
    our options are basically Try and Either, right? And we can mix them
    up like
    so:

    import scala.util.Try
    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    Wouldn't it be good then to spend a couple more words on this duo than
    Try's scaladoc currently does? The only mention of Either is the
    slightly
    nebulous: "It's similar to, but semantically different from the
    scala.util.Either type." I'd prefer if the API docs for the relevant
    types
    helped to give new users a quick high-level overview of error
    handling in
    Scala so they'd know where to turn for further reading.

    Cheers,
    Erik


    --
    Daniel C. Sobral

    I travel to the future all the time.
  • √iktor Ҡlang at Oct 2, 2012 at 3:31 pm

    On Tue, Oct 2, 2012 at 4:19 PM, Josh Suereth wrote:

    Just a note: exception handling code is impure and outside the realm of
    the general category we use when we define monads. Try actually fulfills
    the monad laws when you use this category.

    When we talk monads, it's important to understand the category, otherwise
    our talk is meaningless.

    In any case, Validation is not for expression friendly if it allows
    aggregation. You have to use alternative grouping operations before a for,
    like zip for collections.

    I think Rex has a very good point here in general. Error handling has
    different requirements. While I want to see a Validation in the std. Lib,
    it's not there now. Either and Try solve some problems but by no means
    encompass the entire problem.

    I do believe that Validation is great for error handling, and I'd like to
    see such a class exist in the std. To round out the offerings.
    Exactly, there's always the possibility of
    Try[Either[SuccessfulValue,FailureValue]]

    On Oct 1, 2012 4:18 PM, "Rex Kerr" wrote:

    Error reporting has the illusion of being simple. But, as you're finding
    out, it is anything but. You can either hack together something arbitrary,
    or you have to worry about the generalizations of the concepts you
    require--namely monads and such. In particular, the "magic" that makes for
    work is not magic at all, but adherence to an appropriate general interface.

    Now, it turns out that there are several really desirable properties
    which are mutually incompatible. Either can be right biased--which would
    make it work with for-comprehensions in some ways--but since it has no
    zero, it cannot work with the filtering capability of for (e.g. for (x <- e
    if x > 5) yield x+2--this works if e is an option, but cannot if e is a
    right-biased Either). There are also monad laws (that proclaim among other
    things the irrelevancy of order of flatMap operations) that conflict with
    what I call the bulletproof property of error reporting, which is that if
    you have a Try[A], and you do an operation that results in a Try[B], you
    will never throw an exception instead of catching it.

    I think, actually, that opinion was more strongly in favor of
    right-biasing Either than not, which would make it more like Try. But
    there are still two huge differences. One is that Try does have an
    effective zero (actually, a zero-type inhabited by infinitely many values)
    so it can be filtered; also Try can obey the bulletproof property while
    Either doesn't even attempt to catch exceptions.

    So, if you want to teach this without going into all the details, you can
    simply say:
    * If you're worried about throwing exceptions and want to keep track of
    them, use a Try.
    * If you want to keep track of either of two possibilities--maybe an
    error state or a correct result--use Either.
    * You may want to repackage one into the other.

    If you come up with questions like, "Why can't I store a database record
    in Failure?", the answer is, "you can hack it so it does, but that isn't
    designed to be type-safe". If you keep asking "why, why, why", eventually
    you get to the point where you have to worry about monads, zeros, and
    right-vs.-no bias. If you don't keep asking why, you can notice that it
    just works as designed--just like Option can only hold zero or one values,
    not more, and aside from that limitation, it works just fine.

    --Rex
    On Mon, Oct 1, 2012 at 2:07 PM, Bruce Eckel wrote:

    I suspect Try has the toOption because that *does* produce something
    that can be used (stepping very hesitatingly as I say this) "monadically"
    whereas Either cannot. Except as you say if you actually force the ...
    projection?

    Again, argh. How come I have to know things about projections and monads
    and all just to understand error reporting? The fundamental problem with
    learning Scala might be that it requires a deep dive into everything in
    order to write something basic.

    Repeat apologies for my frustration. I feel like I've been circling this
    problem for months now.


    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Mon, Oct 1, 2012 at 11:34 AM, Daniel Sobral wrote:

    I'd suggest using Try(3).toEither.right, but I've just noticed Try has
    a toOption, but not a toEither. A serious oversight, imho.

    On Mon, Oct 1, 2012 at 1:48 PM, Bruce Eckel <bruceteckel@gmail.com>
    wrote:
    Your example doesn't work in 2.10 M7:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:11: error: type mismatch;
    found : scala.util.Try[Int]
    required: scala.util.Either[?,?]
    b <- Try(3)
    ^

    Removing the .right produces a possibly more illuminating error:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    for {
    a <- Right(5)
    b <- Try(3)
    } yield a + b

    // Exiting paste mode, now interpreting.

    <console>:10: error: value flatMap is not a member of
    scala.util.Right[Nothing,Int]
    a <- Right(5)
    ^
    Thus, you can't use Either in the same way you can use Option or Try inside
    a for comprehension: Either has no flatmap and is thus (trying out my
    slowly-acreting monadic knowledge here) not monadic. Which makes it seem
    like an even odder choice for error reporting, and odder still that the
    ScalaDoc for Either says "A common use of Either is as an alternative to
    scala.Option for dealing with possible missing values." Either seems to have
    very little of the abilities of Option.

    Roland, when you said "My understanding is that that horse is quite dead
    ;-)" (in regards to Either) can you clarify whether that means "we've all
    agreed to use Either for error reporting and have moved on" or "we've all
    agreed that the use of Either for error reporting was a really poor choice
    and are tired of discussing it"? If the latter, shouldn't we be
    discussing
    its replacement?

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com



    On Mon, Oct 1, 2012 at 6:04 AM, Erik Post <eriksensei@gmail.com>
    wrote:
    Hi Roland,
    On Sunday, 30 September 2012 20:39:00 UTC+2, rkuhn wrote:

    Maybe your point is really about the choice of naming: as it
    stands, Try
    is a specific leaf node (see more below) in the failure handling
    concept
    hierarchy. Validation is a different leaf node here, Either might be
    regarded as another one, and you are after their parent. The
    problem with
    unifying e.g. Try and Validation is that Try is fail-fast while
    Validation
    tries all operations and aggregates the failures.

    So if we restrict ourselves to the Scala standard library for a
    moment,
    our options are basically Try and Either, right? And we can mix them
    up like
    so:

    import scala.util.Try
    for {
    a <- Right(5).right
    b <- Try(3)
    } yield a + b

    Wouldn't it be good then to spend a couple more words on this duo
    than
    Try's scaladoc currently does? The only mention of Either is the
    slightly
    nebulous: "It's similar to, but semantically different from the
    scala.util.Either type." I'd prefer if the API docs for the relevant
    types
    helped to give new users a quick high-level overview of error
    handling in
    Scala so they'd know where to turn for further reading.

    Cheers,
    Erik


    --
    Daniel C. Sobral

    I travel to the future all the time.

    --
    Viktor Klang

    Akka Tech Lead
    Typesafe <http://www.typesafe.com/> - The software stack for applications
    that scale

    Twitter: @viktorklang
  • Alex Cruise at Oct 2, 2012 at 5:09 pm

    On Tue, Oct 2, 2012 at 8:31 AM, √iktor Ҡlang wrote:

    Exactly, there's always the possibility of
    Try[Either[SuccessfulValue,FailureValue]]
    Yeah, that's the way I see it too. If you want to catch exceptions *or*
    return some kind of value, without messy try/catch syntax, use Try. If
    that non-exception type turns out to be a ADT like Either or Option, so
    much the better.

    On the other hand, if you're ignoring exceptions, e.g. because you're
    already in a context where they're handled for you (*cough* akka2 *ahem*),
    and you want to return "either" one-of-these or one-of-those, Either is the
    perfect idiomatic choice.

    -0xe1a

    PS: Every time a thread about Try comes up, this song starts playing in my
    head for a few hours: http://www.youtube.com/watch?v=4HCoRjyw9OI -- ignore
    the image. :)
  • Tim Pigden at Oct 3, 2012 at 9:51 am
    +1 on Validation in standard lib - please!. scalaz is great (and I use
    Validation all the time) but it's not "pick up and use" friendly - and has
    a reputation for being difficult which (deserved or not) may put many off
    who just need that one feature.
    On 2 October 2012 15:19, Josh Suereth wrote:

    Just a note: exception handling code is impure and outside the realm of
    the general category we use when we define monads. Try actually fulfills
    the monad laws when you use this category.

    When we talk monads, it's important to understand the category, otherwise
    our talk is meaningless.

    In any case, Validation is not for expression friendly if it allows
    aggregation. You have to use alternative grouping operations before a for,
    like zip for collections.

    I think Rex has a very good point here in general. Error handling has
    different requirements. While I want to see a Validation in the std. Lib,
    it's not there now. Either and Try solve some problems but by no means
    encompass the entire problem.

    I do believe that Validation is great for error handling, and I'd like to
    see such a class exist in the std. To round out the offerings.
  • Daniel Sobral at Oct 2, 2012 at 12:37 am

    On Mon, Oct 1, 2012 at 3:07 PM, Bruce Eckel wrote:
    I suspect Try has the toOption because that *does* produce something that
    can be used (stepping very hesitatingly as I say this) "monadically" whereas
    Either cannot. Except as you say if you actually force the ... projection?
    Option is not a monad. It *has* a monad, which is provided directly by
    the class.

    Either has *two* monads, both of which are useful depending on what
    you need. That's why you call .right or .left on Either to use it on
    for-comprehensions: to select the monad you want to work with.

    There's no reason whatsoever not to have toEither, and, in fact, one
    can easily do so with fold -- which is also missing. :-)
    Again, argh. How come I have to know things about projections and monads and
    all just to understand error reporting? The fundamental problem with
    learning Scala might be that it requires a deep dive into everything in
    order to write something basic.
    Well, you don't. I learned it without any knowledge of monads. In
    fact, I think it's better to learn the stuff *before* figuring out
    monads.

    --
    Daniel C. Sobral

    I travel to the future all the time.
  • Bruce Eckel at Oct 1, 2012 at 6:07 pm
    https://groups.google.com/d/topic/scala-debate/K6wv6KphJK0/discussion
    https://groups.google.com/d/topic/scala-debate/XlN-oqbslS0/discussion

    OK, those links were very useful. I'll mention some things:

    1) Heather Miller points out "Though, a bottom-line here is, of course,
    that Try can be used outside of the context of Futures." (which disagrees
    with a number of comments I've gotten that say Try is only for Futures). A
    similar comment: "`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`."

    2) As mentioned elsewhere, Josh has noted that Failure requires an
    Exception argument, and all the attendant baggage. Which argues against its
    use to replace Either for error reporting.

    There seem to be two camps:

    (A) the early adopters who seem to be able to absorb all things equally
    easily, and have no trouble mapping names to concepts. These folks seem to
    feel that Left and Right are perfectly reasonably ways to report errors;
    some even go as far as to say that "names have no meaning."

    (B) The less-assured and less-vocal, to whom things don't instantly make
    sense. I argue that this is the vast majority of your users -- maybe not
    now, but these are the masses that you hope will eventually adopt this
    language. To these people, the use of Left and Right to report errors seems
    ... odd, to say the least, and certainly counterintuitive. They argue (as I
    do) not for the continuation of the "Either experiment" but instead to use
    meaningful names to report errors.

    I'll say it again: Error handling is one of the fundamental parts of
    programming. Trying to explain the state of Scala error handling as it
    exists, to a beginner, falls somewhere between quite difficult and
    downright embarrassing. Some of the arguments I've seen in support of
    further obfuscation *should* be embarrassing, but are instead taken
    seriously. I come away from all of this truly wondering about the state of
    the language -- it can do some amazing things, but at the same time it
    seems like something as fundamental as errors has not been thought out, and
    is being driven in seemingly random directions. It's quite frustrating; at
    times I think there is tremendous possibility in Scala, but then I see
    things like this and wonder if it can ever reach the state where an average
    programmer can actually grasp it. If they have to jump through as many
    hoops as I have to figure out how to deal with something as basic as
    errors, then I am not so hopeful.

    I suppose it doesn't help that I am also looking at Go, where they have put
    tremendous focus on simplicity, consistency and the beginner experience,
    and thought about errors (and how to report them simply and in a way that
    doesn't cross process boundaries) from the start.

    Apologies for venting. As I say, it's frustrating.

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com
  • Pagoda_5b at Oct 3, 2012 at 9:25 am
    Hi Bruce,

    I would point out that explaining error handling in java can seem a simple
    enough task, since you only need to understand the concept of try/catch and
    exceptions.
    As the years in the field have shown, though, while easy to explain, real
    usage of checked exceptions can become burdensome, ineffective, and at the
    very end even useless. (the introduction of *TryWithResource* syntax is
    clear enough evidence of this).
    In the end, java's take on error handling didn't fully stand the test of
    time.

    Now, haskell (and maybe other functional languages that I'm missing) has
    been dealing with errors using Either for many years, as the accepted
    solution.

    Comes scala and you find yourself in the uneasy situation of having to deal
    with Exceptions from the java legacy code, while trying for a more
    consistent solution to error handling.
    You're not starting afresh here because there's java inadequate exception
    mechanism to deal with.

    Of course Either was an easy adoption for already acquainted functional
    programmers, but the power of composition through monadic behavior comes
    with the additional burden of using projections.

    Twitter found a composable enough solution to Exception handling with Try.

    I think that right now we're in the middle of a shifting situation with no
    stable and well-defined solution to the problem at hand, so there's no
    clear error handling method for the newcomer.
    I'd like, as Josh already stated, that a well-though and stable
    Validation-like solution will eventually become part of the scala standard
    library.

    I guess that you'd like to see more work in progress in this area maybe,
    being such a critical aspect of programming.

    Does it check?

    Bye,
    Ivano
    On Monday, October 1, 2012 8:00:49 PM UTC+2, Bruce Eckel wrote:


    I'll say it again: Error handling is one of the fundamental parts of
    programming. Trying to explain the state of Scala error handling as it
    exists, to a beginner, falls somewhere between quite difficult and
    downright embarrassing. Some of the arguments I've seen in support of
    further obfuscation *should* be embarrassing, but are instead taken
    seriously. I come away from all of this truly wondering about the state of
    the language -- it can do some amazing things, but at the same time it
    seems like something as fundamental as errors has not been thought out, and
    is being driven in seemingly random directions. It's quite frustrating; at
    times I think there is tremendous possibility in Scala, but then I see
    things like this and wonder if it can ever reach the state where an average
    programmer can actually grasp it. If they have to jump through as many
    hoops as I have to figure out how to deal with something as basic as
    errors, then I am not so hopeful.

    Apologies for venting. As I say, it's frustrating.

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com

  • Matthew Pocock at Oct 3, 2012 at 9:47 am

    On 3 October 2012 10:24, pagoda_5b wrote:

    Hi Bruce,

    I would point out that explaining error handling in java can seem a simple
    enough task, since you only need to understand the concept of try/catch and
    exceptions.
    As the years in the field have shown, though, while easy to explain, real
    usage of checked exceptions can become burdensome, ineffective, and at the
    very end even useless. (the introduction of *TryWithResource* syntax is
    clear enough evidence of this).
    In the end, java's take on error handling didn't fully stand the test of
    time.
    I agree. IMHO, this is because raising an exception on fault lulls you into
    a false sense of security, abnegating all responsibility for describing the
    fault while getting a warm fuzzy feeling that you've done something about
    it.

    Now, haskell (and maybe other functional languages that I'm missing) has
    been dealing with errors using Either for many years, as the accepted
    solution.
    Exceptions are the accepted solution in Java, Either in haskell, and so on.
    However, returning an Either[String, T] to log an error condition is even
    less useful than capturing a stack-trace - where did the Either come from,
    and how did we get there? The root cause problem with handling errors is
    that we spend all our time thinking very carefully about the success type,
    but hardly any time at all thinking about the error type.

    If you embrace code-as-proof, then just as the success type is a proof of
    success, the error type should be a proof of fault. String doesn't bring
    much to the table when you view errors like this. It is better to return a
    structure that contains values that witness what went wrong. For example,
    if the index was out of bounds, you should return a structure that asserts
    that the index must be in bounds, gives the bounds and also the index.
    Bonus points if you can decorate this with provenance for the bounds and/or
    index.

    I've started taking this approach for gnarley code that deals with values
    that can be in- or out-of-range, particularly if the code calling into it
    can't always be expected to know up-front what values are in-range. Rather
    than just logging, returning Option, Either[String, T], or raising an
    exception, I've been carefully constructing the 'out of range' proof and
    returning Validation[OutOfRangeProof, T]. This approach seems to be serving
    me well so far. In production, these proofs get logged. In interactive
    debug mode, I can rapidly take these proofs and use the values embedded in
    them to work out what has gone wrong and where.

    Matthew

    Bye,
    Ivano
    --
    Dr Matthew Pocock
    Integrative Bioinformatics Group, School of Computing Science, Newcastle
    University
    mailto: turingatemyhamster@gmail.com
    mailto: matthew.pocock@ncl.ac.uk
    gchat: turingatemyhamster@gmail.com
    msn: matthew_pocock@yahoo.co.uk
    irc.freenode.net: drdozer
    skype: matthew.pocock
    tel: (0191) 2566550
    mob: +447535664143
  • Tim Pigden at Oct 3, 2012 at 9:56 am
    Indeed. I use a family of structured objects for the failure side of the
    Validation. I have one that handles exceptions. I also have a system of
    implicit ValidationContext objects which manage a kind of pseudo-stack of
    place markers so I can trace down into the bowels of the validating code.

    Tim
    On 3 October 2012 10:47, Matthew Pocock wrote:


    On 3 October 2012 10:24, pagoda_5b wrote:

    Hi Bruce,

    I would point out that explaining error handling in java can seem a
    simple enough task, since you only need to understand the concept of
    try/catch and exceptions.
    As the years in the field have shown, though, while easy to explain, real
    usage of checked exceptions can become burdensome, ineffective, and at the
    very end even useless. (the introduction of *TryWithResource* syntax is
    clear enough evidence of this).
    In the end, java's take on error handling didn't fully stand the test of
    time.
    I agree. IMHO, this is because raising an exception on fault lulls you
    into a false sense of security, abnegating all responsibility for
    describing the fault while getting a warm fuzzy feeling that you've done
    something about it.

    Now, haskell (and maybe other functional languages that I'm missing) has
    been dealing with errors using Either for many years, as the accepted
    solution.
    Exceptions are the accepted solution in Java, Either in haskell, and so
    on. However, returning an Either[String, T] to log an error condition is
    even less useful than capturing a stack-trace - where did the Either come
    from, and how did we get there? The root cause problem with handling errors
    is that we spend all our time thinking very carefully about the success
    type, but hardly any time at all thinking about the error type.

    If you embrace code-as-proof, then just as the success type is a proof of
    success, the error type should be a proof of fault. String doesn't bring
    much to the table when you view errors like this. It is better to return a
    structure that contains values that witness what went wrong. For example,
    if the index was out of bounds, you should return a structure that asserts
    that the index must be in bounds, gives the bounds and also the index.
    Bonus points if you can decorate this with provenance for the bounds and/or
    index.

    I've started taking this approach for gnarley code that deals with values
    that can be in- or out-of-range, particularly if the code calling into it
    can't always be expected to know up-front what values are in-range. Rather
    than just logging, returning Option, Either[String, T], or raising an
    exception, I've been carefully constructing the 'out of range' proof and
    returning Validation[OutOfRangeProof, T]. This approach seems to be serving
    me well so far. In production, these proofs get logged. In interactive
    debug mode, I can rapidly take these proofs and use the values embedded in
    them to work out what has gone wrong and where.

    Matthew
  • Matthew Pocock at Oct 3, 2012 at 10:04 am

    On 3 October 2012 10:56, Tim Pigden wrote:

    Indeed. I use a family of structured objects for the failure side of the
    Validation. I have one that handles exceptions. I also have a system of
    implicit ValidationContext objects which manage a kind of pseudo-stack of
    place markers so I can trace down into the bowels of the validating code.

    Interesting. Mulling over this in the shower, I was thinking that what we
    often need when tracing bugs is provenance-carrying values - not just x or
    where x was shown to be faulty, but where x was calculated, and for each
    value it depends upon, their provenance also. Values can originate quite a
    long way away from the stack-trace on which they trigger failure. Your
    ValidationContext implicits sound like they capture something similar to
    this.

    Matthew

    Tim

    On 3 October 2012 10:47, Matthew Pocock wrote:


    On 3 October 2012 10:24, pagoda_5b wrote:

    Hi Bruce,

    I would point out that explaining error handling in java can seem a
    simple enough task, since you only need to understand the concept of
    try/catch and exceptions.
    As the years in the field have shown, though, while easy to explain, real
    usage of checked exceptions can become burdensome, ineffective, and at the
    very end even useless. (the introduction of *TryWithResource* syntax is
    clear enough evidence of this).
    In the end, java's take on error handling didn't fully stand the test of
    time.
    I agree. IMHO, this is because raising an exception on fault lulls you
    into a false sense of security, abnegating all responsibility for
    describing the fault while getting a warm fuzzy feeling that you've done
    something about it.

    Now, haskell (and maybe other functional languages that I'm missing) has
    been dealing with errors using Either for many years, as the accepted
    solution.
    Exceptions are the accepted solution in Java, Either in haskell, and so
    on. However, returning an Either[String, T] to log an error condition is
    even less useful than capturing a stack-trace - where did the Either come
    from, and how did we get there? The root cause problem with handling errors
    is that we spend all our time thinking very carefully about the success
    type, but hardly any time at all thinking about the error type.

    If you embrace code-as-proof, then just as the success type is a proof of
    success, the error type should be a proof of fault. String doesn't bring
    much to the table when you view errors like this. It is better to return a
    structure that contains values that witness what went wrong. For example,
    if the index was out of bounds, you should return a structure that asserts
    that the index must be in bounds, gives the bounds and also the index.
    Bonus points if you can decorate this with provenance for the bounds and/or
    index.

    I've started taking this approach for gnarley code that deals with values
    that can be in- or out-of-range, particularly if the code calling into it
    can't always be expected to know up-front what values are in-range. Rather
    than just logging, returning Option, Either[String, T], or raising an
    exception, I've been carefully constructing the 'out of range' proof and
    returning Validation[OutOfRangeProof, T]. This approach seems to be serving
    me well so far. In production, these proofs get logged. In interactive
    debug mode, I can rapidly take these proofs and use the values embedded in
    them to work out what has gone wrong and where.

    Matthew

    --
    Dr Matthew Pocock
    Integrative Bioinformatics Group, School of Computing Science, Newcastle
    University
    mailto: turingatemyhamster@gmail.com
    mailto: matthew.pocock@ncl.ac.uk
    gchat: turingatemyhamster@gmail.com
    msn: matthew_pocock@yahoo.co.uk
    irc.freenode.net: drdozer
    skype: matthew.pocock
    tel: (0191) 2566550
    mob: +447535664143
  • Bruce Eckel at Oct 4, 2012 at 10:28 am
    Yes! Whatever you return to indicate failure should contain information
    about that failure that's useful to the caller.

    It's as if we're trying to move from simply getting callers to pay
    attention to errors -- exceptions do this, albeit in a heavy-handed way --
    to producing information about the error that's more than just "where it
    happened."

    Putting as much logic about the error condition as possible into the
    failure indication might not always be possible, but it seems like it could
    greatly aid troubleshooting.

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Wed, Oct 3, 2012 at 3:47 AM, Matthew Pocock wrote:


    On 3 October 2012 10:24, pagoda_5b wrote:

    Hi Bruce,

    I would point out that explaining error handling in java can seem a
    simple enough task, since you only need to understand the concept of
    try/catch and exceptions.
    As the years in the field have shown, though, while easy to explain, real
    usage of checked exceptions can become burdensome, ineffective, and at the
    very end even useless. (the introduction of *TryWithResource* syntax is
    clear enough evidence of this).
    In the end, java's take on error handling didn't fully stand the test of
    time.
    I agree. IMHO, this is because raising an exception on fault lulls you
    into a false sense of security, abnegating all responsibility for
    describing the fault while getting a warm fuzzy feeling that you've done
    something about it.

    Now, haskell (and maybe other functional languages that I'm missing) has
    been dealing with errors using Either for many years, as the accepted
    solution.
    Exceptions are the accepted solution in Java, Either in haskell, and so
    on. However, returning an Either[String, T] to log an error condition is
    even less useful than capturing a stack-trace - where did the Either come
    from, and how did we get there? The root cause problem with handling errors
    is that we spend all our time thinking very carefully about the success
    type, but hardly any time at all thinking about the error type.

    If you embrace code-as-proof, then just as the success type is a proof of
    success, the error type should be a proof of fault. String doesn't bring
    much to the table when you view errors like this. It is better to return a
    structure that contains values that witness what went wrong. For example,
    if the index was out of bounds, you should return a structure that asserts
    that the index must be in bounds, gives the bounds and also the index.
    Bonus points if you can decorate this with provenance for the bounds and/or
    index.

    I've started taking this approach for gnarley code that deals with values
    that can be in- or out-of-range, particularly if the code calling into it
    can't always be expected to know up-front what values are in-range. Rather
    than just logging, returning Option, Either[String, T], or raising an
    exception, I've been carefully constructing the 'out of range' proof and
    returning Validation[OutOfRangeProof, T]. This approach seems to be serving
    me well so far. In production, these proofs get logged. In interactive
    debug mode, I can rapidly take these proofs and use the values embedded in
    them to work out what has gone wrong and where.

    Matthew

    Bye,
    Ivano
    --
    Dr Matthew Pocock
    Integrative Bioinformatics Group, School of Computing Science, Newcastle
    University
    mailto: turingatemyhamster@gmail.com
    mailto: matthew.pocock@ncl.ac.uk
    gchat: turingatemyhamster@gmail.com
    msn: matthew_pocock@yahoo.co.uk
    irc.freenode.net: drdozer
    skype: matthew.pocock
    tel: (0191) 2566550
    mob: +447535664143
  • Alec Zorab at Oct 4, 2012 at 12:42 pm
    I mostly agree, but I think that there's a continuum of errors - at the one
    end we have OutOfMemoryError and co, which represent general, irrecoverable
    death and destruction. At what might be the opposite end we have Option[A],
    which signals "you wanted something that's not available, I presume you
    know what to do when that happens", which doesn't really need much more
    context.

    The validation contexts thing sounds pretty cool though, I can definitely
    see a use for this kind of thing in system making heavy use of Akka or
    similar, where your stack traces usually carry little-to-no information
    about where the values you're looking at came from.
    On 4 October 2012 11:28, Bruce Eckel wrote:

    Yes! Whatever you return to indicate failure should contain information
    about that failure that's useful to the caller.

    It's as if we're trying to move from simply getting callers to pay
    attention to errors -- exceptions do this, albeit in a heavy-handed way --
    to producing information about the error that's more than just "where it
    happened."

    Putting as much logic about the error condition as possible into the
    failure indication might not always be possible, but it seems like it could
    greatly aid troubleshooting.


    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com



    On Wed, Oct 3, 2012 at 3:47 AM, Matthew Pocock <
    turingatemyhamster@gmail.com> wrote:
    On 3 October 2012 10:24, pagoda_5b wrote:

    Hi Bruce,

    I would point out that explaining error handling in java can seem a
    simple enough task, since you only need to understand the concept of
    try/catch and exceptions.
    As the years in the field have shown, though, while easy to explain, real
    usage of checked exceptions can become burdensome, ineffective, and at the
    very end even useless. (the introduction of *TryWithResource* syntax is
    clear enough evidence of this).
    In the end, java's take on error handling didn't fully stand the test of
    time.
    I agree. IMHO, this is because raising an exception on fault lulls you
    into a false sense of security, abnegating all responsibility for
    describing the fault while getting a warm fuzzy feeling that you've done
    something about it.

    Now, haskell (and maybe other functional languages that I'm missing) has
    been dealing with errors using Either for many years, as the accepted
    solution.
    Exceptions are the accepted solution in Java, Either in haskell, and so
    on. However, returning an Either[String, T] to log an error condition is
    even less useful than capturing a stack-trace - where did the Either come
    from, and how did we get there? The root cause problem with handling errors
    is that we spend all our time thinking very carefully about the success
    type, but hardly any time at all thinking about the error type.

    If you embrace code-as-proof, then just as the success type is a proof of
    success, the error type should be a proof of fault. String doesn't bring
    much to the table when you view errors like this. It is better to return a
    structure that contains values that witness what went wrong. For example,
    if the index was out of bounds, you should return a structure that asserts
    that the index must be in bounds, gives the bounds and also the index.
    Bonus points if you can decorate this with provenance for the bounds and/or
    index.

    I've started taking this approach for gnarley code that deals with values
    that can be in- or out-of-range, particularly if the code calling into it
    can't always be expected to know up-front what values are in-range. Rather
    than just logging, returning Option, Either[String, T], or raising an
    exception, I've been carefully constructing the 'out of range' proof and
    returning Validation[OutOfRangeProof, T]. This approach seems to be serving
    me well so far. In production, these proofs get logged. In interactive
    debug mode, I can rapidly take these proofs and use the values embedded in
    them to work out what has gone wrong and where.

    Matthew

    Bye,
    Ivano
    --
    Dr Matthew Pocock
    Integrative Bioinformatics Group, School of Computing Science, Newcastle
    University
    mailto: turingatemyhamster@gmail.com
    mailto: matthew.pocock@ncl.ac.uk
    gchat: turingatemyhamster@gmail.com
    msn: matthew_pocock@yahoo.co.uk
    irc.freenode.net: drdozer
    skype: matthew.pocock
    tel: (0191) 2566550
    mob: +447535664143
  • Bruce Eckel at Oct 4, 2012 at 4:14 pm
    I would call it a landscape rather than a continuum, because there's an
    abrupt drop off when you get to exceptions. Exceptions are the relatively
    rare case where we actually can't do anything about the problem in any
    nearby/knowable scope. But using exceptions to report every error (a la
    Java) is like pulling the fire alarm when the dog soils the carpet. It's
    inappropriate overkill. What Try does is corral these inappropriate
    error-reportings back in to the realm of reasonability. But we occasionally
    still run into these exceptional cases -- note that Go originally didn't
    have exceptions, but then later added "Panic" (good name; emphasizes the
    special case-ness) because it was necessary in a few situations. But all
    other non-panic error reporting in Go seems to be accomplished with tuples,
    which satisfy the requirement that you can't really ignore them (just like
    disjoint unions).

    That's what I'm looking for in Scala -- a unified error-reporting mechanism
    for non-exceptional errors. Which is why I was disappointed in Try, because
    Success and Failure seem like a good solution, except that for some reason
    Failure is tied only to exceptions, incurring the overhead when it's not
    necessary and effectively forcing try to only be used to convert
    exceptions. I still wonder whether an additional Failure type could be
    added that carries non-exceptions.

    I also don't understand the argument that "Try is only for Futures." I see
    that Try was motivated by Futures, but why create an error mechanism that
    is only for one thing when you could create a general-purpose system? Why
    continue creating a bunch of different approaches and confusing the
    landscape?

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Thu, Oct 4, 2012 at 6:35 AM, Alec Zorab wrote:

    I mostly agree, but I think that there's a continuum of errors - at the
    one end we have OutOfMemoryError and co, which represent general,
    irrecoverable death and destruction. At what might be the opposite end we
    have Option[A], which signals "you wanted something that's not available, I
    presume you know what to do when that happens", which doesn't really need
    much more context.

    The validation contexts thing sounds pretty cool though, I can definitely
    see a use for this kind of thing in system making heavy use of Akka or
    similar, where your stack traces usually carry little-to-no information
    about where the values you're looking at came from.

    On 4 October 2012 11:28, Bruce Eckel wrote:

    Yes! Whatever you return to indicate failure should contain information
    about that failure that's useful to the caller.

    It's as if we're trying to move from simply getting callers to pay
    attention to errors -- exceptions do this, albeit in a heavy-handed way --
    to producing information about the error that's more than just "where it
    happened."

    Putting as much logic about the error condition as possible into the
    failure indication might not always be possible, but it seems like it could
    greatly aid troubleshooting.


    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com



    On Wed, Oct 3, 2012 at 3:47 AM, Matthew Pocock <
    turingatemyhamster@gmail.com> wrote:
    On 3 October 2012 10:24, pagoda_5b wrote:

    Hi Bruce,

    I would point out that explaining error handling in java can seem a
    simple enough task, since you only need to understand the concept of
    try/catch and exceptions.
    As the years in the field have shown, though, while easy to explain,
    real usage of checked exceptions can become burdensome, ineffective, and at
    the very end even useless. (the introduction of *TryWithResource*syntax is clear enough evidence of this).
    In the end, java's take on error handling didn't fully stand the test
    of time.
    I agree. IMHO, this is because raising an exception on fault lulls you
    into a false sense of security, abnegating all responsibility for
    describing the fault while getting a warm fuzzy feeling that you've done
    something about it.

    Now, haskell (and maybe other functional languages that I'm missing)
    has been dealing with errors using Either for many years, as the accepted
    solution.
    Exceptions are the accepted solution in Java, Either in haskell, and so
    on. However, returning an Either[String, T] to log an error condition is
    even less useful than capturing a stack-trace - where did the Either come
    from, and how did we get there? The root cause problem with handling errors
    is that we spend all our time thinking very carefully about the success
    type, but hardly any time at all thinking about the error type.

    If you embrace code-as-proof, then just as the success type is a proof
    of success, the error type should be a proof of fault. String doesn't bring
    much to the table when you view errors like this. It is better to return a
    structure that contains values that witness what went wrong. For example,
    if the index was out of bounds, you should return a structure that asserts
    that the index must be in bounds, gives the bounds and also the index.
    Bonus points if you can decorate this with provenance for the bounds and/or
    index.

    I've started taking this approach for gnarley code that deals with
    values that can be in- or out-of-range, particularly if the code calling
    into it can't always be expected to know up-front what values are in-range.
    Rather than just logging, returning Option, Either[String, T], or raising
    an exception, I've been carefully constructing the 'out of range' proof and
    returning Validation[OutOfRangeProof, T]. This approach seems to be serving
    me well so far. In production, these proofs get logged. In interactive
    debug mode, I can rapidly take these proofs and use the values embedded in
    them to work out what has gone wrong and where.

    Matthew

    Bye,
    Ivano
    --
    Dr Matthew Pocock
    Integrative Bioinformatics Group, School of Computing Science, Newcastle
    University
    mailto: turingatemyhamster@gmail.com
    mailto: matthew.pocock@ncl.ac.uk
    gchat: turingatemyhamster@gmail.com
    msn: matthew_pocock@yahoo.co.uk
    irc.freenode.net: drdozer
    skype: matthew.pocock
    tel: (0191) 2566550
    mob: +447535664143
  • Rex Kerr at Oct 4, 2012 at 6:28 pm

    On Thu, Oct 4, 2012 at 12:14 PM, Bruce Eckel wrote:

    That's what I'm looking for in Scala -- a unified error-reporting
    mechanism for non-exceptional errors. Which is why I was disappointed in
    Try, because Success and Failure seem like a good solution, except that for
    some reason Failure is tied only to exceptions, incurring the overhead when
    it's not necessary and effectively forcing try to only be used to convert
    exceptions. I still wonder whether an additional Failure type could be
    added that carries non-exceptions.

    Are you proposing

    trait Try[S,E] { ... }
    case class Success[S,E](result: S) extends Try[S,E] { ... }
    case class Erronious[S,E](error: E) extends Try[S,E] { ... }
    case class Failure[S,E](exception: Throwable) extends Try[S,E] { ... }

    so that you can have return values _or_ caught exceptions _or_ a custom
    non-success payload of your own devising?

    --Rex
  • Bruce Eckel at Oct 4, 2012 at 8:29 pm
    Assuming that could be made to work, something along those lines.

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Thu, Oct 4, 2012 at 12:28 PM, Rex Kerr wrote:
    On Thu, Oct 4, 2012 at 12:14 PM, Bruce Eckel wrote:

    That's what I'm looking for in Scala -- a unified error-reporting
    mechanism for non-exceptional errors. Which is why I was disappointed in
    Try, because Success and Failure seem like a good solution, except that for
    some reason Failure is tied only to exceptions, incurring the overhead when
    it's not necessary and effectively forcing try to only be used to convert
    exceptions. I still wonder whether an additional Failure type could be
    added that carries non-exceptions.

    Are you proposing

    trait Try[S,E] { ... }
    case class Success[S,E](result: S) extends Try[S,E] { ... }
    case class Erronious[S,E](error: E) extends Try[S,E] { ... }
    case class Failure[S,E](exception: Throwable) extends Try[S,E] { ... }

    so that you can have return values _or_ caught exceptions _or_ a custom
    non-success payload of your own devising?

    --Rex
  • √iktor Ҡlang at Oct 4, 2012 at 10:12 pm

    On Thu, Oct 4, 2012 at 8:28 PM, Rex Kerr wrote:
    On Thu, Oct 4, 2012 at 12:14 PM, Bruce Eckel wrote:

    That's what I'm looking for in Scala -- a unified error-reporting
    mechanism for non-exceptional errors. Which is why I was disappointed in
    Try, because Success and Failure seem like a good solution, except that for
    some reason Failure is tied only to exceptions, incurring the overhead when
    it's not necessary and effectively forcing try to only be used to convert
    exceptions. I still wonder whether an additional Failure type could be
    added that carries non-exceptions.

    Are you proposing

    trait Try[S,E] { ... }
    case class Success[S,E](result: S) extends Try[S,E] { ... }
    case class Erronious[S,E](error: E) extends Try[S,E] { ... }
    case class Failure[S,E](exception: Throwable) extends Try[S,E] { ... }

    so that you can have return values _or_ caught exceptions _or_ a custom
    non-success payload of your own devising?

    What would .get return? What would recover do? What happens if an exception
    is thrown when you have Erronious and do recover?
    What is the advantage over Try[Either[S, E]] ?


    --Rex

    --
    Viktor Klang

    Akka Tech Lead
    Typesafe <http://www.typesafe.com/> - The software stack for applications
    that scale

    Twitter: @viktorklang
  • Rex Kerr at Oct 4, 2012 at 11:11 pm
    That's what I was trying to get at--if you think about how to solve the
    problem, rather than merely noting that a nontrivial task is indeed
    not-so-trivial, is there a major improvement in usage to be had?

    I am not convinced that there's much improvement over Try[Either[E,S]]
    (let's keep the left-is-worse-than-right convention if we're using
    Either!), but if Bruce had something in mind it may be worth considering.

    It is entirely possible to make Success/Erronious/Failure work, but I'm not
    sure that it actually makes things easier to use, since there are many
    non-obvious behaviors that one would have to remember, plus either a large
    number of method names to move between different conditions, or
    boilerplatey user code to handle it.

    (For example, you probably want to be able to get a Success out of either
    an Erronious or a Failure or both. What do you call those three methods?
    One takes f: E => S, one g: Throwable => S, and one both. And you probably
    want to be able to promote your Failures to Erronious, so now you have four
    methods all with the general idea of "make it better". And you can't
    overload because of (1) erasure and (2) E might be Throwable.)

    I think it'd feel more like two similar concepts treading clumsily on each
    others' toes than a single elegant mechanism (since inherently there is not
    one mechanism for non-success states). I'm open to alternative views if
    they come with compelling examples.

    This isn't to say the current error-handling facilities are the be-all and
    end-all of error-handling, but it does call into question a "big tent"
    solution.

    --Rex
    On Thu, Oct 4, 2012 at 6:12 PM, √iktor Ҡlang wrote:


    On Thu, Oct 4, 2012 at 8:28 PM, Rex Kerr wrote:
    On Thu, Oct 4, 2012 at 12:14 PM, Bruce Eckel wrote:

    That's what I'm looking for in Scala -- a unified error-reporting
    mechanism for non-exceptional errors. Which is why I was disappointed in
    Try, because Success and Failure seem like a good solution, except that for
    some reason Failure is tied only to exceptions, incurring the overhead when
    it's not necessary and effectively forcing try to only be used to convert
    exceptions. I still wonder whether an additional Failure type could be
    added that carries non-exceptions.

    Are you proposing

    trait Try[S,E] { ... }
    case class Success[S,E](result: S) extends Try[S,E] { ... }
    case class Erronious[S,E](error: E) extends Try[S,E] { ... }
    case class Failure[S,E](exception: Throwable) extends Try[S,E] { ... }

    so that you can have return values _or_ caught exceptions _or_ a custom
    non-success payload of your own devising?

    What would .get return? What would recover do? What happens if an
    exception is thrown when you have Erronious and do recover?
    What is the advantage over Try[Either[S, E]] ?


    --Rex

    --
    Viktor Klang

    Akka Tech Lead
    Typesafe <http://www.typesafe.com/> - The software stack for applications
    that scale

    Twitter: @viktorklang
  • Will Sargent at Dec 28, 2012 at 4:53 am
    Hi all,

    I've made a stab at summarizing some of the concepts in error
    handling: let me know if it's clear and / or egregiously wrong.

    http://tersesystems.com/2012/12/27/error-handling-in-scala

    Will.
    On Thu, Oct 4, 2012 at 4:05 PM, Rex Kerr wrote:
    That's what I was trying to get at--if you think about how to solve the
    problem, rather than merely noting that a nontrivial task is indeed
    not-so-trivial, is there a major improvement in usage to be had?

    I am not convinced that there's much improvement over Try[Either[E,S]]
    (let's keep the left-is-worse-than-right convention if we're using Either!),
    but if Bruce had something in mind it may be worth considering.

    It is entirely possible to make Success/Erronious/Failure work, but I'm not
    sure that it actually makes things easier to use, since there are many
    non-obvious behaviors that one would have to remember, plus either a large
    number of method names to move between different conditions, or boilerplatey
    user code to handle it.

    (For example, you probably want to be able to get a Success out of either an
    Erronious or a Failure or both. What do you call those three methods? One
    takes f: E => S, one g: Throwable => S, and one both. And you probably want
    to be able to promote your Failures to Erronious, so now you have four
    methods all with the general idea of "make it better". And you can't
    overload because of (1) erasure and (2) E might be Throwable.)

    I think it'd feel more like two similar concepts treading clumsily on each
    others' toes than a single elegant mechanism (since inherently there is not
    one mechanism for non-success states). I'm open to alternative views if
    they come with compelling examples.

    This isn't to say the current error-handling facilities are the be-all and
    end-all of error-handling, but it does call into question a "big tent"
    solution.

    --Rex

    On Thu, Oct 4, 2012 at 6:12 PM, √iktor Ҡlang wrote:


    On Thu, Oct 4, 2012 at 8:28 PM, Rex Kerr wrote:

    On Thu, Oct 4, 2012 at 12:14 PM, Bruce Eckel <bruceteckel@gmail.com>
    wrote:
    That's what I'm looking for in Scala -- a unified error-reporting
    mechanism for non-exceptional errors. Which is why I was disappointed in
    Try, because Success and Failure seem like a good solution, except that for
    some reason Failure is tied only to exceptions, incurring the overhead when
    it's not necessary and effectively forcing try to only be used to convert
    exceptions. I still wonder whether an additional Failure type could be added
    that carries non-exceptions.

    Are you proposing

    trait Try[S,E] { ... }
    case class Success[S,E](result: S) extends Try[S,E] { ... }
    case class Erronious[S,E](error: E) extends Try[S,E] { ... }
    case class Failure[S,E](exception: Throwable) extends Try[S,E] { ...
    }

    so that you can have return values _or_ caught exceptions _or_ a custom
    non-success payload of your own devising?

    What would .get return? What would recover do? What happens if an
    exception is thrown when you have Erronious and do recover?
    What is the advantage over Try[Either[S, E]] ?


    --Rex


    --
    Viktor Klang

    Akka Tech Lead
    Typesafe - The software stack for applications that scale

    Twitter: @viktorklang
  • Eric Kolotyluk at Dec 28, 2012 at 6:07 am
    Excellent read - thanks.

    Cheers, Eric
    On 2012-12-27 8:52 PM, Will Sargent wrote:
    Hi all,

    I've made a stab at summarizing some of the concepts in error
    handling: let me know if it's clear and / or egregiously wrong.

    http://tersesystems.com/2012/12/27/error-handling-in-scala

    Will.
    On Thu, Oct 4, 2012 at 4:05 PM, Rex Kerr wrote:
    That's what I was trying to get at--if you think about how to solve the
    problem, rather than merely noting that a nontrivial task is indeed
    not-so-trivial, is there a major improvement in usage to be had?

    I am not convinced that there's much improvement over Try[Either[E,S]]
    (let's keep the left-is-worse-than-right convention if we're using Either!),
    but if Bruce had something in mind it may be worth considering.

    It is entirely possible to make Success/Erronious/Failure work, but I'm not
    sure that it actually makes things easier to use, since there are many
    non-obvious behaviors that one would have to remember, plus either a large
    number of method names to move between different conditions, or boilerplatey
    user code to handle it.

    (For example, you probably want to be able to get a Success out of either an
    Erronious or a Failure or both. What do you call those three methods? One
    takes f: E => S, one g: Throwable => S, and one both. And you probably want
    to be able to promote your Failures to Erronious, so now you have four
    methods all with the general idea of "make it better". And you can't
    overload because of (1) erasure and (2) E might be Throwable.)

    I think it'd feel more like two similar concepts treading clumsily on each
    others' toes than a single elegant mechanism (since inherently there is not
    one mechanism for non-success states). I'm open to alternative views if
    they come with compelling examples.

    This isn't to say the current error-handling facilities are the be-all and
    end-all of error-handling, but it does call into question a "big tent"
    solution.

    --Rex

    On Thu, Oct 4, 2012 at 6:12 PM, √iktor Ҡlang wrote:

    On Thu, Oct 4, 2012 at 8:28 PM, Rex Kerr wrote:
    On Thu, Oct 4, 2012 at 12:14 PM, Bruce Eckel <bruceteckel@gmail.com>
    wrote:
    That's what I'm looking for in Scala -- a unified error-reporting
    mechanism for non-exceptional errors. Which is why I was disappointed in
    Try, because Success and Failure seem like a good solution, except that for
    some reason Failure is tied only to exceptions, incurring the overhead when
    it's not necessary and effectively forcing try to only be used to convert
    exceptions. I still wonder whether an additional Failure type could be added
    that carries non-exceptions.
    Are you proposing

    trait Try[S,E] { ... }
    case class Success[S,E](result: S) extends Try[S,E] { ... }
    case class Erronious[S,E](error: E) extends Try[S,E] { ... }
    case class Failure[S,E](exception: Throwable) extends Try[S,E] { ...
    }

    so that you can have return values _or_ caught exceptions _or_ a custom
    non-success payload of your own devising?
    What would .get return? What would recover do? What happens if an
    exception is thrown when you have Erronious and do recover?
    What is the advantage over Try[Either[S, E]] ?

    --Rex

    --
    Viktor Klang

    Akka Tech Lead
    Typesafe - The software stack for applications that scale

    Twitter: @viktorklang
  • Som Snytt at Dec 28, 2012 at 8:13 am

    On Thu, Dec 27, 2012 at 8:52 PM, Will Sargent wrote:

    Hi all,

    I've made a stab at summarizing some of the concepts in error
    handling: let me know if it's clear and / or egregiously wrong.

    http://tersesystems.com/2012/12/27/error-handling-in-scala

    Will.
    On Thu, Oct 4, 2012 at 4:05 PM, Rex Kerr wrote:

    (let's keep the left-is-worse-than-right convention if we're using Either!),
    As Rex says, left=sinister is a cultural convention; worth noting that
    it's not enforced.

    The blog is terse; I don't know about egregious, but here's a note:

    A couple of times Viktor Klang has rushed to the defense of transform, so
    despite the lack of something called something like finally:

    import scala.util._
    import java.io.{ Console => _, _ }

    object Test extends App {
    def errorHandler(e: Exception) = Console println s"Oops, ${e.getMessage}"
    implicit class Lastly[A](val trying: Try[A]) extends AnyVal {
    def goodly(f: =>Unit)(a: A) = { f; trying }
    def badly(f: =>Unit)(t: Throwable) = { f; trying }
    def lastly(f: =>Unit) = trying transform (goodly(f)(_), badly(f)(_))
    }
    val file = "tryfinally.txt"
    val input = new BufferedReader(new FileReader(file))
    Try {
    Iterator continually input.readLine() takeWhile (_ != null)
    foreach (Console println _)
    } lastly {
    input.close
    } recover {
    case e: IOException => errorHandler(e)
    }
    }

    I thought I had dealt with this previously, but couldn't find anything in
    my scratch dir; so perhaps the fact that I had to look goes to the question
    whether it's documented how to do something finally.

    I used the following sample text file; it cracks me up for some reason, or
    at least cracks a smile:

    Now is
    the time
    for all good
    dogs to come
    and sit stay.
  • √iktor Ҡlang at Dec 28, 2012 at 10:23 am

    On Fri, Dec 28, 2012 at 9:12 AM, Som Snytt wrote:
    On Thu, Dec 27, 2012 at 8:52 PM, Will Sargent wrote:

    Hi all,

    I've made a stab at summarizing some of the concepts in error
    handling: let me know if it's clear and / or egregiously wrong.

    http://tersesystems.com/2012/12/27/error-handling-in-scala

    Will.
    On Thu, Oct 4, 2012 at 4:05 PM, Rex Kerr wrote:

    (let's keep the left-is-worse-than-right convention if we're using Either!),
    As Rex says, left=sinister is a cultural convention; worth noting that
    it's not enforced.

    The blog is terse; I don't know about egregious, but here's a note:

    A couple of times Viktor Klang has rushed to the defense of transform, so
    despite the lack of something called something like finally:
    That Viktor seems like a cool guy. ;-)

    implicit class TryOps[T](val t: Try[T]) extends AnyVal {
    def eventually[Ignore](effect: => Ignore): Try[T] = t.transform(_ => {
    effect; t }, _ => { effect; t })
    }

    Try(1 / 0).map(_ + 1) eventually { println("Oppa Gangnam Style") }

    import scala.util._
    import java.io.{ Console => _, _ }

    object Test extends App {
    def errorHandler(e: Exception) = Console println s"Oops, ${e.getMessage}"
    implicit class Lastly[A](val trying: Try[A]) extends AnyVal {
    def goodly(f: =>Unit)(a: A) = { f; trying }
    def badly(f: =>Unit)(t: Throwable) = { f; trying }
    def lastly(f: =>Unit) = trying transform (goodly(f)(_), badly(f)(_))
    }
    val file = "tryfinally.txt"
    val input = new BufferedReader(new FileReader(file))
    Try {
    Iterator continually input.readLine() takeWhile (_ != null)
    foreach (Console println _)
    } lastly {
    input.close
    } recover {
    case e: IOException => errorHandler(e)
    }
    }

    I thought I had dealt with this previously, but couldn't find anything in
    my scratch dir; so perhaps the fact that I had to look goes to the question
    whether it's documented how to do something finally.

    I used the following sample text file; it cracks me up for some reason, or
    at least cracks a smile:

    Now is
    the time
    for all good
    dogs to come
    and sit stay.


    --
    Viktor Klang

    Director of Engineering
    Typesafe <http://www.typesafe.com/> - The software stack for applications
    that scale

    Twitter: @viktorklang
  • Som Snytt at Dec 28, 2012 at 9:16 pm

    On Fri, Dec 28, 2012 at 2:23 AM, √iktor Ҡlang wrote:

    implicit class TryOps[T](val t: Try[T]) extends AnyVal {
    def eventually[Ignore](effect: => Ignore): Try[T] = t.transform(_ => {
    effect; t }, _ => { effect; t })
    }

    Try(1 / 0).map(_ + 1) eventually { println("Oppa Gangnam Style") }


    As a footnote to the canonical form, refactoring the anonfun saves a class
    and a few opcodes, and also changes the name from
    TryOps$$anonfun$eventually$extension$1 to TryOps$$anonfun$1.

    Echoing another thread, it would nice if repl told us what names are
    generated, and provided short-cuts. I already thought of :javap -app Test
    to show Test$delayedInit$body as that fruit hangs so low.

    implicit class TryOps[T](val t: Try[T]) extends AnyVal {
    def eventually[Ignore](effect: => Ignore): Try[T] = {
    val ignoring = (_: Any) => { effect; t }
    t transform (ignoring, ignoring)
    }
    }
  • Som Snytt at Dec 28, 2012 at 8:46 am

    On Thu, Dec 27, 2012 at 8:52 PM, Will Sargent wrote:

    Hi all,

    I've made a stab at summarizing some of the concepts in error
    handling: let me know if it's clear and / or egregiously wrong.

    http://tersesystems.com/2012/12/27/error-handling-in-scala
    I didn't understand the objection to returning Try[Something].

    It's simpler than a Validation, but more than an Option. I just had a
    List[Try[_]] because reading a file could succeed or fail. That seems kind
    of simple to me.

    If an intermediary doesn't care for Try, just do an immediate get and it
    will either succeed or throw.

    E.g., List[Try[_]] map (_.get) flattens it or fails.
  • Will Sargent at Dec 29, 2012 at 3:31 am

    I didn't understand the objection to returning Try[Something].

    It's simpler than a Validation, but more than an Option. I just had a
    List[Try[_]] because reading a file could succeed or fail. That seems kind
    of simple to me.

    If an intermediary doesn't care for Try, just do an immediate get and it
    will either succeed or throw.

    E.g., List[Try[_]] map (_.get) flattens it or fails.
    You're right that calling .get will throw an exception, and using Try
    isn't problematic in itself, but... one of the big problems I've had
    in Java code is that given a checked exception, Eclipse would suggest:

    try {
    Foo foo = fooService.getFoo();
    } catch (FooException e) {
    e.printStackTrace();
    }

    whereas if it were an unchecked exception, the code would compile and
    the exception would head silently up the stack where it could be
    handled more appropriately. You could argue that's not a language
    problem, but the issue with error handling constructs is that when
    people are forced to make a choice, some of them are going to do the
    Wrong Thing.

    Updated the blog post to be clearer and less confusing -- thanks for
    being awesome!

    Will.
  • Rex Kerr at Dec 28, 2012 at 9:44 am
    Typos:
    (1) You're missing a comma in your either-fold example (at the end of the
    error case--this isn't a match statement)

    Questions:
    (a) Why do your example recovers (in the Try section) use case e:
    Exception => blah? Unlike try/catch, Try already filters out exceptions
    that you want to pass through (serious JVM faults and exceptions used for
    control-flow), so a simple case e => blah is what you want to "handle
    everything".
    (b) Why do you say that Try only "apparently" lets you cross thread
    boundaries with exceptions? Thrown exceptions don't cross thread
    boundaries. Objects do. If you specifically mean, "Well, why not package
    the exceptions in Either?", maybe you should say so?
    (c) Why do you not mention Option except in the middle of Either? It's
    the most common way to handle expected failure--so much so that we don't
    even notice, I guess? A little nod to it in advance would be good, even if
    you don't care to cover it.

    Comments:
    (i) You should mention up front that Either really just holds either of
    two values (disjoint union, sum type, whatever) but that it is commonly
    used for error handling.
    (ii) There is no advantage to using Either instead of Try in a public API
    if you are going to return a wrapped exception. So I don't think the
    advice to use Either instead of Try is quite what I would offer; instead,
    I'd suggest to use Either if you want to return a *non-exception error state
    * (or simply an alternate valid state).
    (iii) Returning Try instead of letting an exception fly through is an
    alternative to having checked exceptions. That is, it declares
    (implicitly, at least): don't worry about unexpected things going wrong
    here; we've captured them all. If this is what you mean, your API should
    return Try. If this is not what you mean, your API should probably let all
    the unhandled exceptions flow through rather than misleadingly packaging a
    subset of them into a Try.
    (iv) Pointing out up front that Try lets you do heap-based exception
    handling is a great idea! At least personally, that was not the first
    thing that came to my mind when I saw it (but it should have been).

    --Rex

    On Thu, Dec 27, 2012 at 11:52 PM, Will Sargent wrote:

    Hi all,

    I've made a stab at summarizing some of the concepts in error
    handling: let me know if it's clear and / or egregiously wrong.

    http://tersesystems.com/2012/12/27/error-handling-in-scala

    Will.
    On Thu, Oct 4, 2012 at 4:05 PM, Rex Kerr wrote:
    That's what I was trying to get at--if you think about how to solve the
    problem, rather than merely noting that a nontrivial task is indeed
    not-so-trivial, is there a major improvement in usage to be had?

    I am not convinced that there's much improvement over Try[Either[E,S]]
    (let's keep the left-is-worse-than-right convention if we're using Either!),
    but if Bruce had something in mind it may be worth considering.

    It is entirely possible to make Success/Erronious/Failure work, but I'm not
    sure that it actually makes things easier to use, since there are many
    non-obvious behaviors that one would have to remember, plus either a large
    number of method names to move between different conditions, or
    boilerplatey
    user code to handle it.

    (For example, you probably want to be able to get a Success out of either an
    Erronious or a Failure or both. What do you call those three methods? One
    takes f: E => S, one g: Throwable => S, and one both. And you probably want
    to be able to promote your Failures to Erronious, so now you have four
    methods all with the general idea of "make it better". And you can't
    overload because of (1) erasure and (2) E might be Throwable.)

    I think it'd feel more like two similar concepts treading clumsily on each
    others' toes than a single elegant mechanism (since inherently there is not
    one mechanism for non-success states). I'm open to alternative views if
    they come with compelling examples.

    This isn't to say the current error-handling facilities are the be-all and
    end-all of error-handling, but it does call into question a "big tent"
    solution.

    --Rex

    On Thu, Oct 4, 2012 at 6:12 PM, √iktor Ҡlang wrote:


    On Thu, Oct 4, 2012 at 8:28 PM, Rex Kerr wrote:

    On Thu, Oct 4, 2012 at 12:14 PM, Bruce Eckel <bruceteckel@gmail.com>
    wrote:
    That's what I'm looking for in Scala -- a unified error-reporting
    mechanism for non-exceptional errors. Which is why I was disappointed
    in
    Try, because Success and Failure seem like a good solution, except
    that for
    some reason Failure is tied only to exceptions, incurring the
    overhead when
    it's not necessary and effectively forcing try to only be used to
    convert
    exceptions. I still wonder whether an additional Failure type could
    be added
    that carries non-exceptions.

    Are you proposing

    trait Try[S,E] { ... }
    case class Success[S,E](result: S) extends Try[S,E] { ... }
    case class Erronious[S,E](error: E) extends Try[S,E] { ... }
    case class Failure[S,E](exception: Throwable) extends Try[S,E] {
    ...
    }

    so that you can have return values _or_ caught exceptions _or_ a custom
    non-success payload of your own devising?

    What would .get return? What would recover do? What happens if an
    exception is thrown when you have Erronious and do recover?
    What is the advantage over Try[Either[S, E]] ?


    --Rex


    --
    Viktor Klang

    Akka Tech Lead
    Typesafe - The software stack for applications that scale

    Twitter: @viktorklang
  • Will Sargent at Dec 28, 2012 at 10:19 pm

    On Fri, Dec 28, 2012 at 1:44 AM, Rex Kerr wrote:
    Typos:
    (1) You're missing a comma in your either-fold example (at the end of the
    error case--this isn't a match statement)

    Questions:
    (a) Why do your example recovers (in the Try section) use case e:
    Exception => blah? Unlike try/catch, Try already filters out exceptions
    that you want to pass through (serious JVM faults and exceptions used for
    control-flow), so a simple case e => blah is what you want to "handle
    everything".
    I'm trying to be explicit for Scala newbies: type inference is great
    for writing code, but it means the compiler knows more than the reader
    does.

    In this case, it's the wrong thing to do -- it should be
    scala.util.control.NonFatal, not Exception.

    Which brings up the question of whether catching e:Exception is a safe
    thing to do in Scala, given that scala.util.control.ControlThrowable
    can be a RuntimeException. The example in
    http://www.scala-lang.org/api/current/scala/util/control/ControlThrowable.html
    looks like it says "no."
    (b) Why do you say that Try only "apparently" lets you cross thread
    boundaries with exceptions? Thrown exceptions don't cross thread
    boundaries. Objects do. If you specifically mean, "Well, why not package
    the exceptions in Either?", maybe you should say so?
    You're right, it's unclear. It's supposed to say "you can send caught
    exceptions across thread boundaries, which you can't do by throwing
    them."

    It's based off what Ronald Kuhn said: "In the context of Futures, the
    caller is not available on the current stack of execution, which is
    the reason why Try was introduced—to reify the bottom type and be able
    to pass it across thread boundaries to the calling context."

    http://grokbase.com/t/gg/scala-user/129w1rkfar/more-about-try-catching-handling#20120930akr7hvezngsw5xhrwztjpx7smq
    (c) Why do you not mention Option except in the middle of Either? It's
    the most common way to handle expected failure--so much so that we don't
    even notice, I guess? A little nod to it in advance would be good, even if
    you don't care to cover it.
    Good idea. I've added a section to the top, a reference to the cheat
    sheet, and a warning for Some(null).
    Comments:
    (i) You should mention up front that Either really just holds either of
    two values (disjoint union, sum type, whatever) but that it is commonly used
    for error handling. Done.
    (ii) There is no advantage to using Either instead of Try in a public API
    if you are going to return a wrapped exception. So I don't think the advice
    to use Either instead of Try is quite what I would offer; instead, I'd
    suggest to use Either if you want to return a non-exception error state (or
    simply an alternate valid state).
    I was saying exactly that, so I need to make that language clearer. I
    should say "Don't return exceptions through Either. Create a case
    class representing failure `FailResult` and use `Either[FailResult,
    Success]`"
    (iii) Returning Try instead of letting an exception fly through is an
    alternative to having checked exceptions. That is, it declares (implicitly,
    at least): don't worry about unexpected things going wrong here; we've
    captured them all. If this is what you mean, your API should return Try.
    If this is not what you mean, your API should probably let all the unhandled
    exceptions flow through rather than misleadingly packaging a subset of them
    into a Try.
    Thank you, that's the missing piece I was struggling with. Returning
    Try implies that you don't need to wrap the code in a try-catch block.
    The way to guarantee that is:

    def doStuff : Try[Foo] = Try {
    Foo()
    }

    It is like using checked exceptions, and that's the other worry. API
    consumers are going to handle a Try for success if they want the
    value...

    sumTry match {
    case Failure(f) => {
    // do nothing
    }
    case Success(s) => {
    Console.println(s)
    }
    }

    And then they're going to swallow the exception and never log it. You
    can't use a top level exception handler to manage logging effectively
    because you have to rely on the intermediates to use sumTry.get (to
    rethrow the exception) or pass it along. You're putting that much
    more faith in your clients to Do the Right Thing.
    (iv) Pointing out up front that Try lets you do heap-based exception
    handling is a great idea! At least personally, that was not the first thing
    that came to my mind when I saw it (but it should have been).
    Much as I'd like to claim credit for that, it's from Victor Klang:

    http://grokbase.com/p/gg/scala-debate/128svrykfq/design-improvement-for-success-failure

    Will.
  • √iktor Ҡlang at Dec 28, 2012 at 10:30 pm

    On Fri, Dec 28, 2012 at 11:18 PM, Will Sargent wrote:
    On Fri, Dec 28, 2012 at 1:44 AM, Rex Kerr wrote:
    Typos:
    (1) You're missing a comma in your either-fold example (at the end of the
    error case--this isn't a match statement)

    Questions:
    (a) Why do your example recovers (in the Try section) use case e:
    Exception => blah? Unlike try/catch, Try already filters out exceptions
    that you want to pass through (serious JVM faults and exceptions used for
    control-flow), so a simple case e => blah is what you want to "handle
    everything".
    I'm trying to be explicit for Scala newbies: type inference is great
    for writing code, but it means the compiler knows more than the reader
    does.

    In this case, it's the wrong thing to do -- it should be
    scala.util.control.NonFatal, not Exception.

    Which brings up the question of whether catching e:Exception is a safe
    thing to do in Scala, given that scala.util.control.ControlThrowable
    can be a RuntimeException. The example in

    http://www.scala-lang.org/api/current/scala/util/control/ControlThrowable.html
    looks like it says "no."
    (b) Why do you say that Try only "apparently" lets you cross thread
    boundaries with exceptions? Thrown exceptions don't cross thread
    boundaries. Objects do. If you specifically mean, "Well, why not package
    the exceptions in Either?", maybe you should say so?
    You're right, it's unclear. It's supposed to say "you can send caught
    exceptions across thread boundaries, which you can't do by throwing
    them."

    It's based off what Ronald Kuhn said: "In the context of Futures, the
    caller is not available on the current stack of execution, which is
    the reason why Try was introduced—to reify the bottom type and be able
    to pass it across thread boundaries to the calling context."


    http://grokbase.com/t/gg/scala-user/129w1rkfar/more-about-try-catching-handling#20120930akr7hvezngsw5xhrwztjpx7smq
    (c) Why do you not mention Option except in the middle of Either? It's
    the most common way to handle expected failure--so much so that we don't
    even notice, I guess? A little nod to it in advance would be good, even if
    you don't care to cover it.
    Good idea. I've added a section to the top, a reference to the cheat
    sheet, and a warning for Some(null).
    Comments:
    (i) You should mention up front that Either really just holds either of
    two values (disjoint union, sum type, whatever) but that it is commonly used
    for error handling. Done.
    (ii) There is no advantage to using Either instead of Try in a public API
    if you are going to return a wrapped exception. So I don't think the advice
    to use Either instead of Try is quite what I would offer; instead, I'd
    suggest to use Either if you want to return a non-exception error state (or
    simply an alternate valid state).
    I was saying exactly that, so I need to make that language clearer. I
    should say "Don't return exceptions through Either. Create a case
    class representing failure `FailResult` and use `Either[FailResult,
    Success]`"
    (iii) Returning Try instead of letting an exception fly through is an
    alternative to having checked exceptions. That is, it declares
    (implicitly,
    at least): don't worry about unexpected things going wrong here; we've
    captured them all. If this is what you mean, your API should return Try.
    If this is not what you mean, your API should probably let all the unhandled
    exceptions flow through rather than misleadingly packaging a subset of them
    into a Try.
    Thank you, that's the missing piece I was struggling with. Returning
    Try implies that you don't need to wrap the code in a try-catch block.
    The way to guarantee that is:

    def doStuff : Try[Foo] = Try {
    Foo()
    }

    It is like using checked exceptions, and that's the other worry. API
    consumers are going to handle a Try for success if they want the
    value...

    sumTry match {
    case Failure(f) => {
    // do nothing
    }
    case Success(s) => {
    Console.println(s)
    }
    }

    And then they're going to swallow the exception and never log it. You
    can't use a top level exception handler to manage logging effectively
    because you have to rely on the intermediates to use sumTry.get (to
    rethrow the exception) or pass it along. You're putting that much
    more faith in your clients to Do the Right Thing.
    (iv) Pointing out up front that Try lets you do heap-based exception
    handling is a great idea! At least personally, that was not the first thing
    that came to my mind when I saw it (but it should have been).
    Much as I'd like to claim credit for that, it's from Victor Klang:


    http://grokbase.com/p/gg/scala-debate/128svrykfq/design-improvement-for-success-failure
    While I'd love to claim credit for that, it surfaced in a discussion with
    @odersky. :-)

    Cheers,


    Will.


    --
    *Viktor Klang*
    *Director of Engineering*
    *
    *
    Typesafe <http://www.typesafe.com/> - The software stack for applications
    that scale
    Twitter: @viktorklang
  • Bruce Eckel at Oct 4, 2012 at 10:21 am
    I'd call that a very cogent summation of what I've experienced so far.

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com


    On Wed, Oct 3, 2012 at 3:24 AM, pagoda_5b wrote:

    Hi Bruce,

    I would point out that explaining error handling in java can seem a simple
    enough task, since you only need to understand the concept of try/catch and
    exceptions.
    As the years in the field have shown, though, while easy to explain, real
    usage of checked exceptions can become burdensome, ineffective, and at the
    very end even useless. (the introduction of *TryWithResource* syntax is
    clear enough evidence of this).
    In the end, java's take on error handling didn't fully stand the test of
    time.

    Now, haskell (and maybe other functional languages that I'm missing) has
    been dealing with errors using Either for many years, as the accepted
    solution.

    Comes scala and you find yourself in the uneasy situation of having to
    deal with Exceptions from the java legacy code, while trying for a more
    consistent solution to error handling.
    You're not starting afresh here because there's java inadequate exception
    mechanism to deal with.

    Of course Either was an easy adoption for already acquainted functional
    programmers, but the power of composition through monadic behavior comes
    with the additional burden of using projections.

    Twitter found a composable enough solution to Exception handling with Try.

    I think that right now we're in the middle of a shifting situation with no
    stable and well-defined solution to the problem at hand, so there's no
    clear error handling method for the newcomer.
    I'd like, as Josh already stated, that a well-though and stable
    Validation-like solution will eventually become part of the scala standard
    library.

    I guess that you'd like to see more work in progress in this area maybe,
    being such a critical aspect of programming.

    Does it check?

    Bye,
    Ivano
    On Monday, October 1, 2012 8:00:49 PM UTC+2, Bruce Eckel wrote:


    I'll say it again: Error handling is one of the fundamental parts of
    programming. Trying to explain the state of Scala error handling as it
    exists, to a beginner, falls somewhere between quite difficult and
    downright embarrassing. Some of the arguments I've seen in support of
    further obfuscation *should* be embarrassing, but are instead taken
    seriously. I come away from all of this truly wondering about the state of
    the language -- it can do some amazing things, but at the same time it
    seems like something as fundamental as errors has not been thought out, and
    is being driven in seemingly random directions. It's quite frustrating; at
    times I think there is tremendous possibility in Scala, but then I see
    things like this and wonder if it can ever reach the state where an average
    programmer can actually grasp it. If they have to jump through as many
    hoops as I have to figure out how to deal with something as basic as
    errors, then I am not so hopeful.

    Apologies for venting. As I say, it's frustrating.

    -- Bruce Eckel
    www.AtomicScala.com
    www.Reinventing-Business.com
    www.MindviewInc.com

Related Discussions

People

Translate

site design / logo © 2022 Grokbase