Not strictly Lift theory question

68 views
Skip to first unread message

Donald McLean

unread,
Apr 11, 2014, 11:30:18 AM4/11/14
to liftweb
One of the things in Lift that I really like is the "Box" concept (where no result is different from and error).

So one of the things that I'm working on, I have a different sort of situation. I have an operation (such as a validation) and either it will succeed, or it will produce one or more errors.

So, would I use something that would be like the opposite of a box/option where you would have Success, or Failure(error), or just use an Option where None == success?

I'm just curious to know what people think.

Donald

Antonio Salazar Cardozo

unread,
Apr 11, 2014, 11:39:37 AM4/11/14
to lif...@googlegroups.com
We typically use Box for this circumstance, and treat Empty as an unknown error. It's slightly awkward, but keeps the concepts
in the system to a minimum.

Scalaz has validations, which model as Success or Failure. I personally hate that API.

You can also use an Either[T, String], where Left is success and Right is the error. I kind of hate that API too.

Option/None is definitely another option, if you don't need the actual successful value. Indeed, if you
don't need the value of the success, and you're only interested in the error message if there was one,
I'd say that's the cleanest implementation—but I would think about my function names carefully so
that it's evident from the invocation that you're looking for errors. That's not always easy.
Thanks,
Antonio

Erik Post

unread,
Apr 11, 2014, 12:21:37 PM4/11/14
to lif...@googlegroups.com
Hi Don, 

For a quick overview, have a look at this: http://typelevel.org/blog/2014/02/21/error-handling.html.

One thing to be aware of is that Lift's Box[T] is like Either[String, T]. For useful error reporting, you might want a more informative type than String. That's where types such as \/, Validation and Either come in; they don't fix the error type to string, but you can do stuff like this:

sealed trait Error
case class ItemNotFound(key: Int) extends Error
case class ItemAlreadyExists(key: Int) extends Error
case object DatabaseFailure(...) extends Error

def find(key: Int): Validation[Error, Item] = { ... 
  case ... => Success(Item(...))
  case ... => Failure(ItemNotFound(key))
  case _ => Failure(DatabaseFailure)
}

If you want to return multiple errors, you can Stick a NonEmptyList[Error] in there instead of Error. I'm not sure what Antonio 'hates' about this. It's pretty convenient. You can chain \/ 'vertically' inside a for-comprehension, just like Box, while Validation can be used to string validations together 'horizontally'. There are lots of tutorials available, or you might want to drop by the #scalaz irc channel on Freenode.

Cheers,
Erik

Antonio Salazar Cardozo

unread,
Apr 11, 2014, 12:46:15 PM4/11/14
to lif...@googlegroups.com
Please note that Box also supports ParamFailure, which carries an additional object beyond the failure
message to provide more detail. We use this extensively to provide error messages for logs with additional
data that can be used for populating the UI, for example, and it makes Box no longer be conceptually
the same as an Either[String,T] with sugar.

The drawback of ParamFailure is that the param's type is not reflected in the Box's type signature, so
you're relying on unchecked information when you write your pattern matches. In my experience, Box's
API is both less noisy and more pleasant to use, so the tradeoff is worth it for me.
Thanks,
Antonio

Erik Post

unread,
Apr 11, 2014, 4:04:47 PM4/11/14
to lif...@googlegroups.com
Hi Antonio,

On Fri, Apr 11, 2014 at 6:46 PM, Antonio Salazar Cardozo <savedf...@gmail.com> wrote:
Please note that Box also supports ParamFailure, which carries an additional object beyond the failure
message to provide more detail. We use this extensively to provide error messages for logs with additional
data that can be used for populating the UI, for example, and it makes Box no longer be conceptually
the same as an Either[String,T] with sugar.

Right. I ought to have said Either[String, Option[T]] by the way, but like you point out, that still isn't isomorphic.

The drawback of ParamFailure is that the param's type is not reflected in the Box's type signature, so
you're relying on unchecked information when you write your pattern matches.

Yup, a very important difference.

Cheers,
Erik
 
In my experience, Box's
API is both less noisy and more pleasant to use, so the tradeoff is worth it for me.
On Friday, April 11, 2014 12:21:37 PM UTC-4, Erik Post wrote:
Hi Don, 

For a quick overview, have a look at this: http://typelevel.org/blog/2014/02/21/error-handling.html.

One thing to be aware of is that Lift's Box[T] is like Either[String, T]. For useful error reporting, you might want a more informative type than String. That's where types such as \/, Validation and Either come in; they don't fix the error type to string, but you can do stuff like this:

sealed trait Error
case class ItemNotFound(key: Int) extends Error
case class ItemAlreadyExists(key: Int) extends Error
case object DatabaseFailure(...) extends Error

def find(key: Int): Validation[Error, Item] = { ... 
  case ... => Success(Item(...))
  case ... => Failure(ItemNotFound(key))
  case _ => Failure(DatabaseFailure)
}

If you want to return multiple errors, you can Stick a NonEmptyList[Error] in there instead of Error. I'm not sure what Antonio 'hates' about this. It's pretty convenient. You can chain \/ 'vertically' inside a for-comprehension, just like Box, while Validation can be used to string validations together 'horizontally'. There are lots of tutorials available, or you might want to drop by the #scalaz irc channel on Freenode.

Cheers,
Erik

On Friday, April 11, 2014 5:30:18 PM UTC+2, Donald McLean wrote:
One of the things in Lift that I really like is the "Box" concept (where no result is different from and error).

So one of the things that I'm working on, I have a different sort of situation. I have an operation (such as a validation) and either it will succeed, or it will produce one or more errors.

So, would I use something that would be like the opposite of a box/option where you would have Success, or Failure(error), or just use an Option where None == success?

I'm just curious to know what people think.

Donald

--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

---
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/Uj0ENMsMtz4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

ti com

unread,
Apr 13, 2014, 4:07:49 PM4/13/14
to liftweb

One thing is if you use box without empty and you pattern match, you get a warning that you forgot one case. And it's annoying to have dummy match.
So a suggestion is, could there be some sealed trait that only full and failure extending. A subclass of box. Then we could use that on place of box in these types of cases. Then the pattern matching warning would be wiser. And its still box, and interchangeable with plain box.

You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.

Antonio Salazar Cardozo

unread,
Apr 13, 2014, 11:05:29 PM4/13/14
to lif...@googlegroups.com
Hmmm… Interesting thought. So basically a tag trait for those two. I wonder if the type inferencer would
infer that common trait if your function only returned Full or Failure, as opposed to sticking to Box...

The bigger problem is, only APIs that actually know about this extra trait would behave as expected.

The way that we generally deal with this is that Empty is handled the same way in places where we're
mostly only expecting Full or Failure. That lets us abstract that behavior. Failures are also handled
mostly the same way (they'll trigger an S.error of some sort as well as log a message), so most of
that handling is in one place.

But I do acknowledge that situations where you only expect Full or Failure are an annoyance.
Thanks,
Antonio
Hi Antonio,

To unsubscribe from this group and all its topics, send an email to liftweb+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+unsubscribe@googlegroups.com.

ti com

unread,
May 1, 2014, 1:43:36 AM5/1/14
to liftweb

I don't understand the bigger problem, can you explain it better

Also I am pretty sure the type inference will work. It can do some pretty hairy lubs.

To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages