This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Option processing / exception handling tricks

15 replies
ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
There's a class of control-flow problems that I occasionally face that go something like this:

  complicated stuff ( stuff that may fail )

for which the classic solution--which is slow because of stack trace filling, not to mention cumbersome--is:

  try {
    complicated stuff ( if (stuff fails) throw new FailException else stuff )
  }
  catch {
    case fe: FailException => do something sensible
  }

The typical way to get around this is

  (stuff that may be optional).map(complicated stuff (_))

which works fine if (1) the optional stuff can be computed before the complicated stuff, and (2) there's only one optional thing.  If (2) is violated, you end up with flatMaps or, more compactly,

  for (o1 <- optcalc1; o2 <- optcalc2; ...) yield complicated_stuff(o1, o2, ...)

This can be awkward since it makes the calculation of needed values non-local to where they are used, and it may end up violating (1) after all.  You can also just jam the whole thing into the for statement:

  for {
    o1 <- optcalc1
    x1 = complicated_part(o1)
    o2 <- optcalc2
    x2 = complicated_part(o2)
    ...
    xN = complicated_part(oN)
  } yield xN

which is okay if you can break up the complicated stuff into a bunch of sequential parts.

Since none of these have seemed entirely friendly, I've also been doing this:

  object Nope extends scala.util.control.ControlThrowable {}
  def probably[A](f: => A): Option[A] = {
    try { Some(f) }
    catch { case Nope => None }
  }
  class OptionProbablyOkay[A](o: Option[A]) {
    def grab = if (o.isDefined) o.get else throw Nope
  }
  implicit def option_is_probably_fine[A](o: Option[A]) = new OptionProbablyOkay(o)

which lets me do things like

  probably( complicated stuff ( stuff that may be an option.grab ) )

An example:

scala> val oi: Option[Int] = None
oi: Option[Int] = None

scala> val a = Array(1,2,3)
a: Array[Int] = Array(1, 2, 3)

scala> probably(a(oi.grab))
res0: Option[Int] = None

I'm reasonably pleased with how this has been working out, but in addition to sharing the pattern for those who might find it useful, I'm also curious to know if others have control flow tools like this (better than this would be great!).  One downside, of course, of this approach is that the exception might leak out if you grab (or throw Nope) without an enclosing probably, and worse still, you'd have no idea where it leaked because it doesn't generate a stack trace (in fact, it doesn't even generate a novel object).

Any suggestions/improvements/alternatives?

  --Rex

fanf
Joined: 2009-03-17,
User offline. Last seen 2 years 30 weeks ago.
Re: Option processing / exception handling tricks

On 23/12/2011 12:46, Rex Kerr wrote:
> There's a class of control-flow problems that I occasionally face that
> go something like this:
> [...things with exceptions and mapping them to option]
>
> Any suggestions/improvements/alternatives?
>

Lift as something named "tryo" for that :
http://scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/lift...

Scalaz certainly have something to deal with that, but I don't know what
(I suspect it will imply I/O monad and applicative functors ;)

Hope it helps,

H-star Development
Joined: 2010-04-14,
User offline. Last seen 2 years 26 weeks ago.
Re: Option processing / exception handling tricks

you didn't mention Either. didn't you try using it or did it fail very badly? because it seems like a perfect match to your problem :)

-------- Original-Nachricht --------
> Datum: Fri, 23 Dec 2011 06:46:55 -0500
> Von: Rex Kerr
> An: scala-user
> Betreff: [scala-user] Option processing / exception handling tricks

> There's a class of control-flow problems that I occasionally face that go
> something like this:
>
> complicated stuff ( stuff that may fail )
>
> for which the classic solution--which is slow because of stack trace
> filling, not to mention cumbersome--is:
>
> try {
> complicated stuff ( if (stuff fails) throw new FailException else
> stuff
> )
> }
> catch {
> case fe: FailException => do something sensible
> }
>
> The typical way to get around this is
>
> (stuff that may be optional).map(complicated stuff (_))
>
> which works fine if (1) the optional stuff can be computed before the
> complicated stuff, and (2) there's only one optional thing. If (2) is
> violated, you end up with flatMaps or, more compactly,
>
> for (o1 <- optcalc1; o2 <- optcalc2; ...) yield complicated_stuff(o1,
> o2,
> ...)
>
> This can be awkward since it makes the calculation of needed values
> non-local to where they are used, and it may end up violating (1) after
> all. You can also just jam the whole thing into the for statement:
>
> for {
> o1 <- optcalc1
> x1 = complicated_part(o1)
> o2 <- optcalc2
> x2 = complicated_part(o2)
> ...
> xN = complicated_part(oN)
> } yield xN
>
> which is okay if you can break up the complicated stuff into a bunch of
> sequential parts.
>
> Since none of these have seemed entirely friendly, I've also been doing
> this:
>
> object Nope extends scala.util.control.ControlThrowable {}
> def probably[A](f: => A): Option[A] = {
> try { Some(f) }
> catch { case Nope => None }
> }
> class OptionProbablyOkay[A](o: Option[A]) {
> def grab = if (o.isDefined) o.get else throw Nope
> }
> implicit def option_is_probably_fine[A](o: Option[A]) = new
> OptionProbablyOkay(o)
>
> which lets me do things like
>
> probably( complicated stuff ( stuff that may be an option.grab ) )
>
> An example:
>
> scala> val oi: Option[Int] = None
> oi: Option[Int] = None
>
> scala> val a = Array(1,2,3)
> a: Array[Int] = Array(1, 2, 3)
>
> scala> probably(a(oi.grab))
> res0: Option[Int] = None
>
> I'm reasonably pleased with how this has been working out, but in addition
> to sharing the pattern for those who might find it useful, I'm also
> curious
> to know if others have control flow tools like this (better than this
> would
> be great!). One downside, of course, of this approach is that the
> exception might leak out if you grab (or throw Nope) without an enclosing
> probably, and worse still, you'd have no idea where it leaked because it
> doesn't generate a stack trace (in fact, it doesn't even generate a novel
> object).
>
> Any suggestions/improvements/alternatives?
>
> --Rex

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: Option processing / exception handling tricks
Maybe my use case wasn't clear, but Either doesn't help at all; it's exactly the same as Option.

I do, in fact, use Either when I care why something went wrong (not just that something went wrong).  It doesn't help propagate exceptional cases out of a local context, however, without creating stack traces, etc..

  --Rex

On Fri, Dec 23, 2011 at 7:47 AM, Dennis Haupt <h-star@gmx.de> wrote:
you didn't mention Either. didn't you try using it or did it fail very badly? because it seems like a perfect match to your problem :)

-------- Original-Nachricht --------
> Datum: Fri, 23 Dec 2011 06:46:55 -0500
> Von: Rex Kerr <ichoran@gmail.com>
> An: scala-user <scala-user@googlegroups.com>
> Betreff: [scala-user] Option processing / exception handling tricks

> There's a class of control-flow problems that I occasionally face that go
> something like this:
>
>   complicated stuff ( stuff that may fail )
>
> for which the classic solution--which is slow because of stack trace
> filling, not to mention cumbersome--is:
>
>   try {
>     complicated stuff ( if (stuff fails) throw new FailException else
> stuff
> )
>   }
>   catch {
>     case fe: FailException => do something sensible
>   }
>
> The typical way to get around this is
>
>   (stuff that may be optional).map(complicated stuff (_))
>
> which works fine if (1) the optional stuff can be computed before the
> complicated stuff, and (2) there's only one optional thing.  If (2) is
> violated, you end up with flatMaps or, more compactly,
>
>   for (o1 <- optcalc1; o2 <- optcalc2; ...) yield complicated_stuff(o1,
> o2,
> ...)
>
> This can be awkward since it makes the calculation of needed values
> non-local to where they are used, and it may end up violating (1) after
> all.  You can also just jam the whole thing into the for statement:
>
>   for {
>     o1 <- optcalc1
>     x1 = complicated_part(o1)
>     o2 <- optcalc2
>     x2 = complicated_part(o2)
>     ...
>     xN = complicated_part(oN)
>   } yield xN
>
> which is okay if you can break up the complicated stuff into a bunch of
> sequential parts.
>
> Since none of these have seemed entirely friendly, I've also been doing
> this:
>
>   object Nope extends scala.util.control.ControlThrowable {}
>   def probably[A](f: => A): Option[A] = {
>     try { Some(f) }
>     catch { case Nope => None }
>   }
>   class OptionProbablyOkay[A](o: Option[A]) {
>     def grab = if (o.isDefined) o.get else throw Nope
>   }
>   implicit def option_is_probably_fine[A](o: Option[A]) = new
> OptionProbablyOkay(o)
>
> which lets me do things like
>
>   probably( complicated stuff ( stuff that may be an option.grab ) )
>
> An example:
>
> scala> val oi: Option[Int] = None
> oi: Option[Int] = None
>
> scala> val a = Array(1,2,3)
> a: Array[Int] = Array(1, 2, 3)
>
> scala> probably(a(oi.grab))
> res0: Option[Int] = None
>
> I'm reasonably pleased with how this has been working out, but in addition
> to sharing the pattern for those who might find it useful, I'm also
> curious
> to know if others have control flow tools like this (better than this
> would
> be great!).  One downside, of course, of this approach is that the
> exception might leak out if you grab (or throw Nope) without an enclosing
> probably, and worse still, you'd have no idea where it leaked because it
> doesn't generate a stack trace (in fact, it doesn't even generate a novel
> object).
>
> Any suggestions/improvements/alternatives?
>
>   --Rex

Bernd Johannes
Joined: 2011-01-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Option processing / exception handling tricks
p, li { white-space: pre-wrap; }

Am Freitag, 23. Dezember 2011, 15:18:14 schrieb Rex Kerr:

> Maybe my use case wasn't clear, but Either doesn't help at all; it's

> exactly the same as Option.

>

> I do, in fact, use Either when I care why something went wrong (not just

> that something went wrong). It doesn't help propagate exceptional cases

> out of a local context, however, without creating stack traces, etc..

>

> --Rex

>


Maybe it's naive - but I always thought that control flow exceptions are a good tool for those cases.

I used it but I never measured their performance first place so it's just a guess.


Have you ever tried "Exception with ControlThrowable"?


Greetings

Bernd





> On Fri, Dec 23, 2011 at 7:47 AM, Dennis Haupt <h-star@gmx.de> wrote:

> > you didn't mention Either. didn't you try using it or did it fail very

> > badly? because it seems like a perfect match to your problem :)

> >

> > -------- Original-Nachricht --------

> >

> > > Datum: Fri, 23 Dec 2011 06:46:55 -0500

> > > Von: Rex Kerr <ichoran@gmail.com>

> > > An: scala-user <scala-user@googlegroups.com>

> > > Betreff: [scala-user] Option processing / exception handling tricks

> > >

> > > There's a class of control-flow problems that I occasionally face that

> > > go

> > >

> > > something like this:

> > > complicated stuff ( stuff that may fail )

> > >

> > > for which the classic solution--which is slow because of stack trace

> > >

> > > filling, not to mention cumbersome--is:

> > > try {

> > >

> > > complicated stuff ( if (stuff fails) throw new FailException else

> > >

> > > stuff

> > > )

> > >

> > > }

> > > catch {

> > >

> > > case fe: FailException => do something sensible

> > >

> > > }

> > >

> > > The typical way to get around this is

> > >

> > > (stuff that may be optional).map(complicated stuff (_))

> > >

> > > which works fine if (1) the optional stuff can be computed before the

> > > complicated stuff, and (2) there's only one optional thing. If (2) is

> > > violated, you end up with flatMaps or, more compactly,

> > >

> > > for (o1 <- optcalc1; o2 <- optcalc2; ...) yield complicated_stuff(o1,

> > >

> > > o2,

> > > ...)

> > >

> > > This can be awkward since it makes the calculation of needed values

> > > non-local to where they are used, and it may end up violating (1) after

> > >

> > > all. You can also just jam the whole thing into the for statement:

> > > for {

> > >

> > > o1 <- optcalc1

> > > x1 = complicated_part(o1)

> > > o2 <- optcalc2

> > > x2 = complicated_part(o2)

> > > ...

> > > xN = complicated_part(oN)

> > >

> > > } yield xN

> > >

> > > which is okay if you can break up the complicated stuff into a bunch of

> > > sequential parts.

> > >

> > > Since none of these have seemed entirely friendly, I've also been doing

> > >

> > > this:

> > > object Nope extends scala.util.control.ControlThrowable {}

> > > def probably[A](f: => A): Option[A] = {

> > >

> > > try { Some(f) }

> > > catch { case Nope => None }

> > >

> > > }

> > > class OptionProbablyOkay[A](o: Option[A]) {

> > >

> > > def grab = if (o.isDefined) o.get else throw Nope

> > >

> > > }

> > > implicit def option_is_probably_fine[A](o: Option[A]) = new

> > >

> > > OptionProbablyOkay(o)

> > >

> > > which lets me do things like

> > >

> > > probably( complicated stuff ( stuff that may be an option.grab ) )

> > >

> > > An example:

> > >

> > > scala> val oi: Option[Int] = None

> > > oi: Option[Int] = None

> > >

> > > scala> val a = Array(1,2,3)

> > > a: Array[Int] = Array(1, 2, 3)

> > >

> > > scala> probably(a(oi.grab))

> > > res0: Option[Int] = None

> > >

> > > I'm reasonably pleased with how this has been working out, but in

> >

> > addition

> >

> > > to sharing the pattern for those who might find it useful, I'm also

> > > curious

> > > to know if others have control flow tools like this (better than this

> > > would

> > > be great!). One downside, of course, of this approach is that the

> > > exception might leak out if you grab (or throw Nope) without an

> > > enclosing probably, and worse still, you'd have no idea where it

> > > leaked because it doesn't generate a stack trace (in fact, it doesn't

> > > even generate a novel object).

> > >

> > > Any suggestions/improvements/alternatives?

> > >

> > > --Rex



ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: Option processing / exception handling tricks
That's exactly what object Nope is.  It's a static ControlThrowable that you throw whenever you find that something has gone wrong.

possibly is just a method that converts that particular exception into Some/None.  (Much like scala.util.control.Exception.catching[classOf[Nope]].opt(...) does, except without needing to specify the caught class.)

  --Rex

On Fri, Dec 23, 2011 at 11:38 AM, Bernd Johannes <bjohanns@bacon.de> wrote:

Am Freitag, 23. Dezember 2011, 15:18:14 schrieb Rex Kerr:

> Maybe my use case wasn't clear, but Either doesn't help at all; it's

> exactly the same as Option.

>

> I do, in fact, use Either when I care why something went wrong (not just

> that something went wrong). It doesn't help propagate exceptional cases

> out of a local context, however, without creating stack traces, etc..

>

> --Rex

>


Maybe it's naive - but I always thought that control flow exceptions are a good tool for those cases.

I used it but I never measured their performance first place so it's just a guess.


Have you ever tried "Exception with ControlThrowable"?


Greetings

Bernd





> On Fri, Dec 23, 2011 at 7:47 AM, Dennis Haupt <h-star@gmx.de> wrote:

> > you didn't mention Either. didn't you try using it or did it fail very

> > badly? because it seems like a perfect match to your problem :)

> >

> > -------- Original-Nachricht --------

> >

> > > Datum: Fri, 23 Dec 2011 06:46:55 -0500

> > > Von: Rex Kerr <ichoran@gmail.com>

> > > An: scala-user <scala-user@googlegroups.com>

> > > Betreff: [scala-user] Option processing / exception handling tricks

> > >

> > > There's a class of control-flow problems that I occasionally face that

> > > go

> > >

> > > something like this:

> > > complicated stuff ( stuff that may fail )

> > >

> > > for which the classic solution--which is slow because of stack trace

> > >

> > > filling, not to mention cumbersome--is:

> > > try {

> > >

> > > complicated stuff ( if (stuff fails) throw new FailException else

> > >

> > > stuff

> > > )

> > >

> > > }

> > > catch {

> > >

> > > case fe: FailException => do something sensible

> > >

> > > }

> > >

> > > The typical way to get around this is

> > >

> > > (stuff that may be optional).map(complicated stuff (_))

> > >

> > > which works fine if (1) the optional stuff can be computed before the

> > > complicated stuff, and (2) there's only one optional thing. If (2) is

> > > violated, you end up with flatMaps or, more compactly,

> > >

> > > for (o1 <- optcalc1; o2 <- optcalc2; ...) yield complicated_stuff(o1,

> > >

> > > o2,

> > > ...)

> > >

> > > This can be awkward since it makes the calculation of needed values

> > > non-local to where they are used, and it may end up violating (1) after

> > >

> > > all. You can also just jam the whole thing into the for statement:

> > > for {

> > >

> > > o1 <- optcalc1

> > > x1 = complicated_part(o1)

> > > o2 <- optcalc2

> > > x2 = complicated_part(o2)

> > > ...

> > > xN = complicated_part(oN)

> > >

> > > } yield xN

> > >

> > > which is okay if you can break up the complicated stuff into a bunch of

> > > sequential parts.

> > >

> > > Since none of these have seemed entirely friendly, I've also been doing

> > >

> > > this:

> > > object Nope extends scala.util.control.ControlThrowable {}

> > > def probably[A](f: => A): Option[A] = {

> > >

> > > try { Some(f) }

> > > catch { case Nope => None }

> > >

> > > }

> > > class OptionProbablyOkay[A](o: Option[A]) {

> > >

> > > def grab = if (o.isDefined) o.get else throw Nope

> > >

> > > }

> > > implicit def option_is_probably_fine[A](o: Option[A]) = new

> > >

> > > OptionProbablyOkay(o)

> > >

> > > which lets me do things like

> > >

> > > probably( complicated stuff ( stuff that may be an option.grab ) )

> > >

> > > An example:

> > >

> > > scala> val oi: Option[Int] = None

> > > oi: Option[Int] = None

> > >

> > > scala> val a = Array(1,2,3)

> > > a: Array[Int] = Array(1, 2, 3)

> > >

> > > scala> probably(a(oi.grab))

> > > res0: Option[Int] = None

> > >

> > > I'm reasonably pleased with how this has been working out, but in

> >

> > addition

> >

> > > to sharing the pattern for those who might find it useful, I'm also

> > > curious

> > > to know if others have control flow tools like this (better than this

> > > would

> > > be great!). One downside, of course, of this approach is that the

> > > exception might leak out if you grab (or throw Nope) without an

> > > enclosing probably, and worse still, you'd have no idea where it

> > > leaked because it doesn't generate a stack trace (in fact, it doesn't

> > > even generate a novel object).

> > >

> > > Any suggestions/improvements/alternatives?

> > >

> > > --Rex




Noel Welsh 2
Joined: 2010-12-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Option processing / exception handling tricks

On Fri, Dec 23, 2011 at 11:46 AM, Rex Kerr wrote:
> The typical way to get around this is
>
>   (stuff that may be optional).map(complicated stuff (_))
>
> which works fine if (1) the optional stuff can be computed before the
> complicated stuff

No reason you can't write

Some(complicated stuff).flatMap(optionalStuff)

Or just use Scalaz's Validation, the gateway drug to Scalaz:

for {
result1 <- complicatedStuff.success
result2 <- stuffThatMayFail
} yield whatever(result1, result2)

Validation is just Either with a stricter convention for which part is
failure and with is success. There are some good tutorials online,
too.

N.

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: Option processing / exception handling tricks
Well--I can see that my original description was not at all clear!

Your suggestion doesn't work for the case I was trying to address because complicated stuff _depends on stuff that may fail_.

Let's take an example of validating an input form.  First, we have unvalidated code (and let's assume that the form itself makes sure the types are correct):

  import java.util.{Arrays => Jua}

  def userSlot(user: User) = {
    records(user.name) -> Jua.binarySearch(timeSlots, user.time)
  }

where records is a map from user names to records, timeSlots is an array of valid times, and you can guess the rest.  You're basically forming a map entry--record to time slot.  Now let's suppose I realize that this can fail because the user name might not exist and the time might not be one of the valid time slots.  Thus, I want this method to return an option.  I could:

  def userSlot(user: User) = {
    val index =Jua.binarySearch(timeSlots, user.time)
    if (index < 0) None
    else records.get(user.name).map(x => x -> index)
  }

which is okay, but it's split the concerns up.  If instead, you could

  def userSlot(user: User) = possibly{
    records.get(user.name).grab -> Jua.binarySearch(timeSlots, user.time).must(_ >= 0)
  }

then you would be able to maintain the same relatively compact form and logic with local conditions.

(Then again, you could argue that it's better to split things up so you don't need to remember so much at once; sometimes this is true.  I'm interested in the cases where it is arguably not true.)

  --Rex

On Fri, Dec 23, 2011 at 2:21 PM, Noel Welsh <noel@untyped.com> wrote:
On Fri, Dec 23, 2011 at 11:46 AM, Rex Kerr <ichoran@gmail.com> wrote:
> The typical way to get around this is
>
>   (stuff that may be optional).map(complicated stuff (_))
>
> which works fine if (1) the optional stuff can be computed before the
> complicated stuff

No reason you can't write

Some(complicated stuff).flatMap(optionalStuff)

Or just use Scalaz's Validation, the gateway drug to Scalaz:

for {
 result1 <- complicatedStuff.success
 result2 <- stuffThatMayFail
} yield whatever(result1, result2)

Validation is just Either with a stricter convention for which part is
failure and with is success. There are some good tutorials online,
too.

N.

--
Noel Welsh
Untyped Ltd                 http://www.untyped.com/
+44 (0) 121 285 2285    info@untyped.com
UK company number    06226466

Tim P
Joined: 2011-07-28,
User offline. Last seen 1 year 4 weeks ago.
Re: Option processing / exception handling tricks

(oops should have sent to group)

If I've understood correctly, I've been doing something broadlysimilar
and using scalaz Validations.What I've found is that being very
specific about which arguments areOption[x] and which are necessary
leaves me with something like
type Validated[T] = Validation[String, T]
for { optX <- getOptionalX y <- getY} yield complicatedThing(optX, y)
def getX: Validated[Option[X]]def getY: Validated[Y]
Now it gets more complicated if you want to do things dependent
onoptX, but in that case I've maintained my visible control flow
byresolving the dependent optional thing in the get action. So if z
isoptional and depends on optX I do
for { optX <- getOptionalX optZ <- getOptionalZFromOptionalX(optX) y
<- getY} yield complicatedThing(optZ, y)
def getOptionalZFromOptionalX(optX: Option[X]): Validated[Option[Z]]
NB they are all using <- rather than = because I'm validating
againstmore catastrophic failures than the option not being there.
What I have found using this approach with Validation is that you
endup threading your Validation through everything which is fine if
youcan live with it.

PS besides tidying up the code the type Validated[T] =
Validation[String, T] (I actually use something other than String)
enables you to eliminate the rather ugly type lambda syntax when using
.sequence as Tony M. pointed out in a throw-away line a few weeks back

Tim

Noel Welsh 2
Joined: 2010-12-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Option processing / exception handling tricks

On Fri, Dec 23, 2011 at 7:53 PM, Rex Kerr wrote:
> Let's take an example of validating an input form.  First, we have
> unvalidated code (and let's assume that the form itself makes sure the types
> are correct):
>
>   import java.util.{Arrays => Jua}
>
>   def userSlot(user: User) = {
>     records(user.name) -> Jua.binarySearch(timeSlots, user.time)
>   }

I guess I don't see what's wrong with:

for {
idx <- binarySearch(timeSlots, user.time)
record <- records(user.name)
} yield (record -> idx)

I write this everywhere, using Scalaz's Validations. My Validation
type is typically

type Validated[T] = Validation[FailCode, T]

Assume both binarySearch and records return Validated

(Using the terminology from Tim's post.)

A FailCode is something I can turn into a response to the user, and
into something I can log (they have different requirements for the
information they contain).

You seem to have an issue with this kind of code. I like have the type
system enforce error handling. If one wanted one could like lots of
operations (e.g. ->) to monads, so you could write

records(user.name) -> binarySearch(timeSlots, user.time)

and it would still return a Validation. This would be a lot of work
and would have odd interactions with Predef.

N.

Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
Re: Option processing / exception handling tricks

Have you seen scala.utility.control.Exceptions.catching ?

val optResult = catching(classOf[NumberFormatException]) opt doFun

On Dec 24, 2011 2:29 PM, "Noel Welsh" <noel@untyped.com> wrote:
On Fri, Dec 23, 2011 at 7:53 PM, Rex Kerr <ichoran@gmail.com> wrote:
> Let's take an example of validating an input form.  First, we have
> unvalidated code (and let's assume that the form itself makes sure the types
> are correct):
>
>   import java.util.{Arrays => Jua}
>
>   def userSlot(user: User) = {
>     records(user.name) -> Jua.binarySearch(timeSlots, user.time)
>   }

I guess I don't see what's wrong with:

for {
 idx <- binarySearch(timeSlots, user.time)
 record <- records(user.name)
} yield (record -> idx)

I write this everywhere, using Scalaz's Validations. My Validation
type is typically

type Validated[T] = Validation[FailCode, T]

Assume both binarySearch and records return Validated

(Using the terminology from Tim's post.)

A FailCode is something I can turn into a response to the user, and
into something I can log (they have different requirements for the
information they contain).

You seem to have an issue with this kind of code. I like have the type
system enforce error handling. If one wanted one could like lots of
operations (e.g. ->) to monads, so you could write

 records(user.name) -> binarySearch(timeSlots, user.time)

and it would still return a Validation. This would be a lot of work
and would have odd interactions with Predef.

N.

--
Noel Welsh
Untyped Ltd                 http://www.untyped.com/
+44 (0) 121 285 2285    info@untyped.com
UK company number    06226466
ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: Option processing / exception handling tricks
Argh, I guess I'm still unclear.  Maybe I should just give up!

On Sat, Dec 24, 2011 at 2:29 PM, Noel Welsh <noel@untyped.com> wrote:
On Fri, Dec 23, 2011 at 7:53 PM, Rex Kerr <ichoran@gmail.com> wrote:
> Let's take an example of validating an input form.  First, we have
> unvalidated code (and let's assume that the form itself makes sure the types
> are correct):
>
>   import java.util.{Arrays => Jua}
>
>   def userSlot(user: User) = {
>     records(user.name) -> Jua.binarySearch(timeSlots, user.time)
>   }

I guess I don't see what's wrong with:

for {
 idx <- binarySearch(timeSlots, user.time)
 record <- records(user.name)
} yield (record -> idx)

What's wrong with this is that records(user.name) is going to throw an exception, not return an option, and binarySearch returns a negative value if the target isn't found, not an option.

Assume both binarySearch and records return Validated

That's exactly the problem--I'm positing that they _don't_ return Validated, and trying to figure out a pattern to deal with this situation.

That is, when I have F[A,M[B]], how do I most effectively build M[F[A,B]]?

One can, of course, use for comprehensions, especially if one has helpful utility methods, e.g.

  class EnrichStuff[A](a: A) {
    def when(f: A => Boolean): Option[A] = //todo
  }
  for {
    idx <- binarySearch(timeSlots, user.time).when(_ >= 0)
    record <- records.get(user.name)
  } yield (record -> idx)

but one can also (as I am proposing in some cases) throw lightweight exceptions for control, e.g.

  class EnrichStuff[A](a: A) {
    def must(f: A => Boolean): A = //todo, throws Nope if false
  }
  class EnrichOption[A](oa: Option[A]) {
    def grab: A = // todo, throws Nope if None
  }
  probably( records.get(user.name).grab -> binarySearch(timeSlots, user.time).must(_ >= 0) )

Whether the one-liner probably or the four-liner for is better depends, in my experience, on the complexity of stuff inside (and whether one needs to keep track of _what_ went wrong or only _that_ something went wrong).  As complexity increases, probably wins.

It's not that different from breakable / break, really.

  --Rex

Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Option processing / exception handling tricks

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 12/27/2011 04:00 AM, Rex Kerr wrote:
> That is, when I have F[A,M[B]], how do I most effectively build M[F[A,B]]?

You use sequence.

This function has an entire article written on it.
http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html

Tony Morris
Joined: 2008-12-19,
User offline. Last seen 30 weeks 4 days ago.
Re: Option processing / exception handling tricks


On Dec 25, 2011 5:29 AM, "Noel Welsh" <noel@untyped.com> wrote:
>
>  records(user.name) -> binarySearch(timeSlots, user.time)

records(user.name) <<*>> binarySearch(timeSlots, user.time)

<<*>> lifts pairing into an applicative functor: F[A] => F[B] => F[(A, B)]

Andreas Scheinert
Joined: 2011-02-11,
User offline. Last seen 42 years 45 weeks ago.
Re: Option processing / exception handling tricks

Hi Rex,
If you choose to pursue the EIP path (IMHO its worth) here a few notes
on it from my point of view.
In eric's post : http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html
He re-implements the necessary abstractions from scratch. Applicative,
Applicative Builder and State (partial type application) makes those
quite verbose. Especially have a look at the table under point
'overview of traversals' it helps greatly with identifying what
functions exactly you may need.
In the paper there is a 'WordCount'-example which demonstrates the
important parts of 'sequence' pretty good.
Here is there example implemented using scalaz6
https://github.com/scalaz/scalaz/blob/master/example/src/main/scala/scal...
and here using scalaz7
https://github.com/scalaz/scalaz/blob/scalaz-seven/example/src/main/scal...

In the end you have to decide yourself if you want to take that heavy
type 'lifting' or use your 'probably' stuff. The nice thing about EIP
is it really builds up (for me). At the bottom you have pure functions
than you add concepts like Monoid, Functor, Applicative Functor and
StateMonad and it all COMPOSES. That is a really nice thing with
FP.Although the WordCount example only involves simple functions and
one State function I think you can use that pretty well to put in your
Validation. I hope this helps and I hope you can share your solution
if you find one that satisfies you.

regards andreas

On 26 Dez., 19:00, Rex Kerr wrote:
> Argh, I guess I'm still unclear.  Maybe I should just give up!
>
>
>
>
>
>
>
>
>
> On Sat, Dec 24, 2011 at 2:29 PM, Noel Welsh wrote:
> > On Fri, Dec 23, 2011 at 7:53 PM, Rex Kerr wrote:
> > > Let's take an example of validating an input form.  First, we have
> > > unvalidated code (and let's assume that the form itself makes sure the
> > types
> > > are correct):
>
> > >   import java.util.{Arrays => Jua}
>
> > >   def userSlot(user: User) = {
> > >     records(user.name) -> Jua.binarySearch(timeSlots, user.time)
> > >   }
>
> > I guess I don't see what's wrong with:
>
> > for {
> >  idx <- binarySearch(timeSlots, user.time)
> >  record <- records(user.name)
> > } yield (record -> idx)
>
> What's wrong with this is that records(user.name) is going to throw an
> exception, not return an option, and binarySearch returns a negative value
> if the target isn't found, not an option.
>
> Assume both binarySearch and records return Validated
>
>
>
> That's exactly the problem--I'm positing that they _don't_ return
> Validated, and trying to figure out a pattern to deal with this situation.
>
> That is, when I have F[A,M[B]], how do I most effectively build M[F[A,B]]?
>
> One can, of course, use for comprehensions, especially if one has helpful
> utility methods, e.g.
>
>   class EnrichStuff[A](a: A) {
>     def when(f: A => Boolean): Option[A] = //todo
>   }
>   for {
>     idx <- binarySearch(timeSlots, user.time).when(_ >= 0)
>     record <- records.get(user.name)
>   } yield (record -> idx)
>
> but one can also (as I am proposing in some cases) throw lightweight
> exceptions for control, e.g.
>
>   class EnrichStuff[A](a: A) {
>     def must(f: A => Boolean): A = //todo, throws Nope if false
>   }
>   class EnrichOption[A](oa: Option[A]) {
>     def grab: A = // todo, throws Nope if None
>   }
>   probably( records.get(user.name).grab -> binarySearch(timeSlots,
> user.time).must(_ >= 0) )
>
> Whether the one-liner probably or the four-liner for is better depends, in
> my experience, on the complexity of stuff inside (and whether one needs to
> keep track of _what_ went wrong or only _that_ something went wrong).  As
> complexity increases, probably wins.
>
> It's not that different from breakable / break, really.
>
>   --Rex

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: Option processing / exception handling tricks
That's a great write-up, thanks for pointing it out (and thanks to Eric for writing it)!

For this particular problem, your/Eric's answer is of the sort
  - First, reconceptualize your entire problem
  - Then, write lots of boilerplate code (Scalaz has done some)
  - Then, everything becomes composable, so compose with appropriate operators
which in certain cases has its merits, but I think it is useful in the opposite spirit  (but same use-cases) as what I was doing.  Namely, if just wrapping things in a for-comprehension doesn't quite cut it, either
  (1) The code was inadequately composable, so use EIP to fix that, or
  (2) Use exceptions to add extra flow control onto the existing structure
where (2) is a particular fix that due to its extra-low overhead is easier than mashing things into a for-comprehension, and (1) is a general fix that despite its extra-high overhead leaves you with many powerful capabilities at your fingertips.

So I could see myself using both methods, but I don't view one as a replacement for the other.  Depends which powerful capabilities my fingertips need.

  --Rex

On Mon, Dec 26, 2011 at 5:00 PM, Tony Morris <tonymorris@gmail.com> wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 12/27/2011 04:00 AM, Rex Kerr wrote:
> That is, when I have F[A,M[B]], how do I most effectively build M[F[A,B]]?

You use sequence.

This function has an entire article written on it.
http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html


- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iQEcBAEBAgAGBQJO+O6IAAoJEPxHMY3rBz0PTwQIAL/fsDSx2/MS9Qis54X/lyr7
87j5rqKQm/eiyT82QfRPocUNP/zGIYR71R3dcgKSkK+blniCbMtfxHTTMIo1VYra
Llj3GyZmJDB6BGH0Z1la5GIsu7ET3eOdGNzkS3uDJ1DDhLE7Xe8ZbTiA7JiXG0tb
zg8bN8xsXDkgvT5jmB9AfnJpYEsszWRa5y0PmoCpzSjwgwlXwQzaWE8inFSEiDp/
MlmC4r7PJQuQ9p2DZnnPnn07ImjW+XVWOdtAXSLefp5Z4OyrFYHxyqc+NpkXH1d7
nIMu/79BX+4E3VIUu/7N3q6QYyTPEcXmNHh57HXUBxFmqiq53TBwvGM1rJWCfkM=
=2RPl
-----END PGP SIGNATURE-----

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland