Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-13 Thread Bertram Felgenhauer
Duncan Coutts wrote:
 Another approach that some people have advocated as a general purpose
 solution is to use:
 
 data Exceptional e a = Exceptional {
   exception :: Maybe e
   result:: a
 }
 
 However it's pretty clear from the structure of this type that it cannot
 cope with lazy error handling in sequences. If you try it you'll find
 you cannot do it without space leaks.

It's not all that clear. Consider this toy example (from a private
discussion with Henning Thielemann a while ago), which runs in
constant space:

import System.IO.Unsafe
import System.Environment
import Control.Monad
import Data.List

data Exceptional e a =
Exceptional { exception :: Maybe e, result :: a }

ok  a = Exceptional Nothing  a
fault e a = Exceptional (Just e) a

faulty :: Int - IO (Exceptional Int [Int])
faulty 0 = return (fault 0 [])
faulty 1 = return (ok [])
faulty n = unsafeInterleaveIO $ do
-- getChar
r - faulty (n-2)
return $ Exceptional (exception r) (n : result r)

main = do
n - readIO . head = getArgs
Exceptional exc res - faulty n
print $ last res
when (n `mod` 3 == 0) $ print exc

This works because ghc's garbage collector evaluates record selectors.
(There are a simpler cases where this matters, for example
last $ fst $ unzip [(a,a) | a - [1..1]]
which also runs in constant space.)

The approach is very fragile, though. For example, if we change main to

main = do
n - readIO . head = getArgs
f - faulty n
print $ last (result f)
when (n `mod` 3 == 0) $ print (exception f)

then the space leak reoccurs - doing the pattern match on the
Excpeptional constructor before using the result is essential.

Bad things also happen if ghc's optimiser turns the record selectors
into explicit pattern matches in the worker ('faulty' in the example).

Kind regards,

Bertram
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-04 Thread wren ng thornton

wren ng thornton wrote:
One of the nice things about not having a Nil is that it lets you easily 
be polymorphic over things ending in () ---a normal list---, (Maybe a) 
---a fallible list---, (Either a b) ---your progress type---, etc. 
Whereas the version that has both Nil and End forces us into the (Maybe 
a) scenario. A side effect of this is that the (Either a b) option isn't 
available because we can only construct t=Mx.(x*t)+(1+a+b) not 
t=Mx.(x*t)+(a+b).


Er, I meant t=Mx.(c*x)+(1+a+b) vs t=Mx.(c*x)+(a+b). This is what I get 
for posting without coffee.


--
Live well,
~wren
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-04 Thread Duncan Coutts
On Thu, 2009-12-03 at 19:49 -0500, wren ng thornton wrote:
 Duncan Coutts wrote:
  I've got an open mind on the suggestion to amalgamate the two ways the
  list could end. I'm not especially in favour of generalising for the
  sake of generalising, especially if it looses the connection to the
  notion of annotating your ordinary data structure with extra errors.
  If I effectively always have to use an Either for the final value then
  perhaps it does not buy anything and just makes the folds uglier (since
  it might loose the connection with the ordinary fold). But it could make
  even that use case simpler so it's worth looking at in a few examples
  (eg the tar package).
 
 These days I view folds as automatically defined by the data type, so I 
 don't see any reason (on those grounds) to want to compare it to lists' 
 foldr as opposed to any other arbitrary catamorphism.

Sure the fold is defined by the data type, except when we are pretending
that one data type is another. This type is intended as a list that is
annotated with errors. So I want to be able to switch between list
versions and this version just by adding an extra error-handling
parameter to the ordinary list fold.

As another example of this, consider the VersionRange type in Cabal. It
provides two different folds depending on what view you want. Neither
matches the underlying constructors exactly.

Duncan

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-04 Thread Jason McCarty
wren ng thornton wrote:

 concat1 :: T a b - (b - T a b) - T a b

This could just as easily be

  concat :: T a b - (b - T a c) - T a c

right? It's a little weird to call this concatenation, but I bet it
could come in handy.

-- 
Jason McCarty jmcca...@sent.com
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-04 Thread David Menendez
On Fri, Dec 4, 2009 at 1:14 PM, Jason McCarty jmcca...@sent.com wrote:
 wren ng thornton wrote:

     concat1 :: T a b - (b - T a b) - T a b

 This could just as easily be

  concat :: T a b - (b - T a c) - T a c

 right? It's a little weird to call this concatenation, but I bet it
 could come in handy.

T a is, among other things, the free monad for the functor (,) a. The
concat you describe is the monadic bind.

data T a b = D b | W a (T a b)

instance Monad (T a) where
return = D

D b = f = f b
W a t = f = W a (t = f)

-- 
Dave Menendez d...@zednenem.com
http://www.eyrie.org/~zednenem/
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-04 Thread wren ng thornton

Jason McCarty wrote:

wren ng thornton wrote:


concat1 :: T a b - (b - T a b) - T a b


This could just as easily be

  concat :: T a b - (b - T a c) - T a c

right? It's a little weird to call this concatenation, but I bet it
could come in handy.


Er right, that's what I meant. (Again the posting without enough coffee 
to pave over the cognitive potholes /chagrin)


--
Live well,
~wren
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-03 Thread Ketil Malde
Duncan Coutts duncan.cou...@googlemail.com writes:

 [1] http://hackage.haskell.org/package/failable-list

 Nice.

I agree this is needed (or rather, would be nice to standardise).  

Although I don't care for the cutesy naming suggested in the 'Train'
datatype, failable-list could be made more general.  Why is there a
specific constructor 'Done', instead of just allowing the user to select
a value of type 'e' (using 'Maybe b' if nothing else works)?

Perhaps we could also consider an infix notation, like:

  data TerminatedList a e = Then a (TerminatedList a e)
  | Finally e

(So you could do e.g:  4 `Then` 5 `Then` 1 `Finally` success!. Of
course, you might prefer symbols instead.) 

-k
-- 
If I haven't seen further, it is by standing in the footprints of giants
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-03 Thread Malcolm Wallace

 data TerminatedList a e = Then a (TerminatedList a e)
 | Finally e


Nice.


(So you could do e.g:  4 `Then` 5 `Then` 1 `Finally` success!.


Errm, you mean: 4 `Then` 5 `Then` 1 `Then` Finally success!


Regards,
Malcolm

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-03 Thread Duncan Coutts
On Thu, 2009-12-03 at 12:34 +0100, Ketil Malde wrote:
 Duncan Coutts duncan.cou...@googlemail.com writes:
 
  [1] http://hackage.haskell.org/package/failable-list
 
  Nice.
 
 I agree this is needed (or rather, would be nice to standardise).  
 
 Although I don't care for the cutesy naming suggested in the 'Train'
 datatype, failable-list could be made more general.  Why is there a
 specific constructor 'Done', instead of just allowing the user to select
 a value of type 'e' (using 'Maybe b' if nothing else works)?
 
 Perhaps we could also consider an infix notation, like:
 
   data TerminatedList a e = Then a (TerminatedList a e)
   | Finally e
 
 (So you could do e.g:  4 `Then` 5 `Then` 1 `Finally` success!. Of
 course, you might prefer symbols instead.) 

I agree the naming could do with some work and it's worth trying a few
variants to see what seems nicest.

I've got an open mind on the suggestion to amalgamate the two ways the
list could end. I'm not especially in favour of generalising for the
sake of generalising, especially if it looses the connection to the
notion of annotating your ordinary data structure with extra errors.
If I effectively always have to use an Either for the final value then
perhaps it does not buy anything and just makes the folds uglier (since
it might loose the connection with the ordinary fold). But it could make
even that use case simpler so it's worth looking at in a few examples
(eg the tar package).

Note that another similar use case is lazy progress reporting. In
cabal-install's dependency solver I've used:

-- | A type to represent the unfolding of an expensive long running
-- calculation that may fail. We may get intermediate steps before the 
-- final result which may be used to indicate progress and/or logging
-- messages.
--
data Progress step fail done = Step step (Progress step fail done)
 | Fail fail
 | Done done

It's a difference in emphasis but I think it may also have a different
Monad instance since we consider Progress to be a single value, not a
list. It's like the Either error monad but with extra writer style
logging.

Duncan

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-03 Thread Ketil Malde
Malcolm Wallace malcolm.wall...@cs.york.ac.uk writes:

 Errm, you mean: 4 `Then` 5 `Then` 1 `Then` Finally success!

Yes, sorry, and thanks.  I guess I should learn to check with ghci
before posting... How about this for a nicer syntax?

  infixr 8 :+
  infixr 8 +:

  data TList a e = a :+ (TList a e)
  | Return e deriving Show
  
  x +: y = x :+ (Return y)

  *Main 2 :+ 4 +: success
  2 :+ (4 :+ Return success)

I like the generic terminal value, it allows things like:

  *Main let count = go 0 where go i (x:xs) = x :+ go (i+1) xs; go i [] = 
Return i
  *Main :t count
  count :: [t] - TList t Integer
  *Main count [1..5]
  1 :+ (2 :+ (3 :+ (4 :+ (5 :+ Return 5

(But perhaps these things can be done more elegantly using State or
similar?) 

-k
-- 
If I haven't seen further, it is by standing in the footprints of giants
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-03 Thread wren ng thornton

Duncan Coutts wrote:

I've got an open mind on the suggestion to amalgamate the two ways the
list could end. I'm not especially in favour of generalising for the
sake of generalising, especially if it looses the connection to the
notion of annotating your ordinary data structure with extra errors.
If I effectively always have to use an Either for the final value then
perhaps it does not buy anything and just makes the folds uglier (since
it might loose the connection with the ordinary fold). But it could make
even that use case simpler so it's worth looking at in a few examples
(eg the tar package).


These days I view folds as automatically defined by the data type, so I 
don't see any reason (on those grounds) to want to compare it to lists' 
foldr as opposed to any other arbitrary catamorphism.


One reason to prefer a single basis is that it simplifies the ability to 
do concatenation and the associated fusion. It still only has one 
termination point (unlike trees) so it's still possible to do augment 
fusion. But because there are two bases, concatenation needs to look 
like this:


concat2 :: T a b - (b - T a b) - T a b - T a b

Whereas for the version with no Nil, it's just:

concat1 :: T a b - (b - T a b) - T a b

As for the usage, we'd be comparing these two:

concat2 foo handler bar

concat1 foo (maybe bar handler)

One of the nice things about not having a Nil is that it lets you easily 
be polymorphic over things ending in () ---a normal list---, (Maybe a) 
---a fallible list---, (Either a b) ---your progress type---, etc. 
Whereas the version that has both Nil and End forces us into the (Maybe 
a) scenario. A side effect of this is that the (Either a b) option isn't 
available because we can only construct t=Mx.(x*t)+(1+a+b) not 
t=Mx.(x*t)+(a+b).


--
Live well,
~wren
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-12-01 Thread Duncan Coutts
On Mon, 2009-11-30 at 20:10 -0800, John Millikin wrote:
 On Mon, Nov 30, 2009 at 03:02, Duncan Coutts
 duncan.cou...@googlemail.com wrote:

  data ListThenError e a = Cons a (ListThenError e a)
  | Error e
 
  Of course this has the disadvantage that then your consumer must
  change to use this type too.
 
  I've been using this list type quite a lot recently. It's in the 'tar'
  package for example. It comes with variants of the standard functions
  foldl, foldr, unfoldr that take into account the error possibility.
 
  At some point we should probably make a package to standardise and
  document this lazy error handling idiom.
 
 Wow, this is perfect! I've extracted that type out into the
 failable-list library[1], with a few added instances for common
 classes (Monad, Applicative, Traversable, etc).
 
 [1] http://hackage.haskell.org/package/failable-list

Nice. The one I've felt is missing in the tar package was a foldl. This
is used to fully consume a failable list. It wants to return either the
normal foldl result or an error encountered.

When consuming with a foldr, the main use case is that you're
translating into another lazy data structure which has it's own place to
annotate errors.

When consuming with a foldl, the main use case is that you're strictly
consuming the list and purging out the errors because you want to
construct a type that does not have room in it for errors.

There seem to be a number of possibilities though:

for reference, standard list foldl:
foldl :: (b - a - b) - b - [a] - b


foldl :: (b - a - b) - b -
  - FailableList e a - Either e b

or the final result as Either (e, b) b

foldl :: (b - a - b) - b - (b - e - b)
  - FailableList e a - b

foldl :: (b - a - b) - b - (b - c) - (b - e - c)
  - FailableList e a - b

This last one is basically the church encoding of Either (e, b) b.

Do we want the partial result at the point the list ended in error? If
not then it's a little simpler.

Duncan

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-11-30 Thread Duncan Coutts
On Mon, 2009-11-30 at 06:08 +, Malcolm Wallace wrote:
 However, if you really want to terminate the stream at  
 the first error, and to reflect this in the type, then I guess you can  
 define your own list type:
 
 data ListThenError e a = Cons a (ListThenError e a)
 | Error e
 
 Of course this has the disadvantage that then your consumer must  
 change to use this type too.

I've been using this list type quite a lot recently. It's in the 'tar'
package for example. It comes with variants of the standard functions
foldl, foldr, unfoldr that take into account the error possibility.

At some point we should probably make a package to standardise and
document this lazy error handling idiom.

Another approach that some people have advocated as a general purpose
solution is to use:

data Exceptional e a = Exceptional {
  exception :: Maybe e
  result:: a
}

However it's pretty clear from the structure of this type that it cannot
cope with lazy error handling in sequences. If you try it you'll find
you cannot do it without space leaks.

Adding the errors directly into the structure seems the best approach to
me. It means the data structure directly reflects the unfolding of the
various possibilities, including errors. It also makes the strictness of
operations much easier to understand. Then we just have to be
comfortable manipulating these data structures using the appropriate
folds and unfolds. Classic pure lazy FP.

Duncan

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-11-30 Thread Bas van Dijk
On Mon, Nov 30, 2009 at 6:22 AM, John Millikin jmilli...@gmail.com wrote:
 ...I've considered two possible error handling modes...

Regarding parsing, there's a third option: iteratees[1]. See [2] for a
motivation and description of iteratees.

regards,

Bas

[1] http://hackage.haskell.org/package/iteratee
[2] http://okmij.org/ftp/Streams.html#iteratee
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-11-30 Thread John Millikin
On Mon, Nov 30, 2009 at 03:02, Duncan Coutts
duncan.cou...@googlemail.com wrote:
 On Mon, 2009-11-30 at 06:08 +, Malcolm Wallace wrote:
 However, if you really want to terminate the stream at
 the first error, and to reflect this in the type, then I guess you can
 define your own list type:

 data ListThenError e a = Cons a (ListThenError e a)
                         | Error e

 Of course this has the disadvantage that then your consumer must
 change to use this type too.

 I've been using this list type quite a lot recently. It's in the 'tar'
 package for example. It comes with variants of the standard functions
 foldl, foldr, unfoldr that take into account the error possibility.

 At some point we should probably make a package to standardise and
 document this lazy error handling idiom.

Wow, this is perfect! I've extracted that type out into the
failable-list library[1], with a few added instances for common
classes (Monad, Applicative, Traversable, etc).

[1] http://hackage.haskell.org/package/failable-list
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-11-29 Thread John Millikin
I'm working on a library which needs to operate on large data sets, so
I'd like to use lazy values. The library consists of pure functions
from Text to [Event] and back. Normally, I use Maybe or Either for
error handling in pure code, but using these precludes lazy
evaluation. Using exceptions requires any errors to be handled in IO,
which is annoying.

The idealized signatures of the functions are:

import qualified Data.Text.Lazy as TL
data Event = EventA | EventB | EventC
parse :: TL.Text - Either ParseError [Event]
serialize :: [Event] - Either SerializeError TL.Text


I've considered two possible error handling modes, both adapted from
procedural language style. The first is simply including errors in the
event list.

import qualified Data.Text as T
parse :: TL.Text - [Either ParseError Event]
serialize :: [Event] - [Either SerializeError T.Text] -- use TL.fromChunks


The second uses monadic callbacks, based on side effects:

parse :: Monad m = (Event - m ()) - (ParseError - m ()) - TL.Text - m ()
serialize :: Monad m = (T.Text - m ()) - (SerializeError - m ())
- [Event] - m ()


The main problem I see with these is that they don't indicate or
enforce that an error terminates the event/text streams. The first
allows multiple errors in a row, or events to follow an error. The
second just feels ugly, because using it in pure code requires
clients to build a Writer (possibly wrapped with ErrorT) and deal with
the associated plumbing.

Is there any sort of standard idiom for handling this problem? It
seems that somebody must have run across it before, but the resources
I can find on lazy error handling all assume the code is impure
(returning IO, etc).
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-11-29 Thread Malcolm Wallace

I'm working on a library which needs to operate on large data sets, so
I'd like to use lazy values. ...

import qualified Data.Text as T
parse :: TL.Text - [Either ParseError Event]



I would say that this is the most desirable approach, if you are  
generating a sequence, and want lazy processing of the elements.   
Indeed, in my own experience, this is the only reasonable way to deal  
with very large datasets, without running out of memory.



The main problem I see with these is that they don't indicate or
enforce that an error terminates the event/text streams. The first
allows multiple errors in a row, or events to follow an error.


Are you sure that there can be no error recovery, to continue with  
events after a mal-formed event has been discarded?  In many cases, it  
is possible.  However, if you really want to terminate the stream at  
the first error, and to reflect this in the type, then I guess you can  
define your own list type:


data ListThenError e a = Cons a (ListThenError e a)
   | Error e

Of course this has the disadvantage that then your consumer must  
change to use this type too.


Regards,
Malcolm

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Are there standard idioms for lazy, pure error handling?

2009-11-29 Thread Luke Palmer
On Sun, Nov 29, 2009 at 11:08 PM, Malcolm Wallace
malcolm.wall...@cs.york.ac.uk wrote:
 Are you sure that there can be no error recovery, to continue with events
 after a mal-formed event has been discarded?  In many cases, it is possible.
  However, if you really want to terminate the stream at the first error, and
 to reflect this in the type, then I guess you can define your own list type:

 data ListThenError e a = Cons a (ListThenError e a)
                       | Error e

 Of course this has the disadvantage that then your consumer must change to
 use this type too.

If it is correct, there is no disadvantage.  Using a list when it is
not the appropriate structure will make both the producer and the
consumer code uglier. You might gain a little notational convenience,
but you bubble implicit assumptions, such as an error terminates a
stream, through the code where they can not be checked.

Of course, when you have a stream from which errors can be recovered,
do not use a type that terminates with errors.

Everything cleans up so nicely when your model perfectly captures your problem.

Luke
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe