A New & Simple Approach: 1. Clump together return object and error object 2. Caller can’t pretend that what they get back is always good How it Works in Go, Scala and Python
At Incorrect inputs, not buggy function or system reliability problems Bad error handling produces bad programs Main problem is novice programmers: “If the compiler or runtime doesn’t tell me I’m doing anything wrong, then I must be doing everything right!” Attitude can persist for a LONG time!
Numerous experiments, nothing worked: Return an unlikely value (like 1) Set a global error flag Raise a signal (that you’ve previously set a signal handler for) s e t j m p /l o n g j m p (nonlocal goto) I could always pretend that I was just getting a good result
Copied C++ Exceptions, except: Enforced the exception specification via checked exceptions Unified error reporting via exceptions Other languages: Python, deeply integrated & (relatively) efficient. Even an endofiteration is indicated with an exception. Ruby Even PHP!
More importantly, Programmer Appropriateness: One size doesn’t fit all. It’s not always best to Panic and throw it all away Jump to a different context Process boundaries Typically, only one exception can be extant at a time
Later added p a n i c and r e s u m e for special cases f u n c O p e n ( n a m e s t r i n g ) ( f i l e * F i l e , e r r e r r o r ) f , e r r : = o s . O p e n ( " f i l e n a m e . e x t " ) i f e r r ! = n i l { l o g . F a t a l ( e r r ) } / / D o s o m e t h i n g w i t h t h e o p e n * F i l e f
p o r t c o m . a t o m i c s c a l a . A t o m i c T e s t . _ d e f f ( i : I n t ) = i f ( i = = 0 ) L e f t ( " D i v i d e b y z e r o " ) e l s e R i g h t ( 2 4 / i ) d e f t e s t ( n : I n t ) = f ( n ) m a t c h { c a s e L e f t ( w h y ) = > s " F a i l e d : $ w h y " c a s e R i g h t ( r e s u l t ) = > r e s u l t } t e s t ( 4 ) i s 6 t e s t ( 5 ) i s 4 t e s t ( 6 ) i s 4 t e s t ( 0 ) i s " F a i l e d : D i v i d e b y z e r o " t e s t ( 2 4 ) i s 1 t e s t ( 2 5 ) i s 0
Scala i m p o r t c o m . a t o m i c s c a l a . A t o m i c T e s t . _ i m p o r t u t i l . { T r y , S u c c e s s } i m p o r t c o m . a t o m i c s c a l a . r e p o r t e r r . F a i l d e f f ( i : I n t ) = i f ( i = = 0 ) F a i l ( " D i v i d e b y z e r o " ) e l s e S u c c e s s ( 2 4 / i ) d e f t e s t ( n : I n t ) = f ( n ) . r e c o v e r { c a s e e = > s " F a i l e d : $ e " } . g e t t e s t ( 4 ) i s 6 t e s t ( 5 ) i s 4 t e s t ( 6 ) i s 4 t e s t ( 0 ) i s " F a i l e d : D i v i d e b y z e r o " t e s t ( 2 4 ) i s 1 t e s t ( 2 5 ) i s 0
as a Result d e f f ( n ) : i f n > 1 0 : r e t u r n n * 1 0 , F a l s e e l s e : r e t u r n N o n e , T r u e d e f t e s t ( n ) : r , f a i l = f ( n ) i f f a i l : p r i n t n , " f a i l e d " e l s e : p r i n t r t e s t ( 9 ) t e s t ( 1 1 ) Not bad, like the Go language.
l a s s R e s u l t ( o b j e c t ) : d e f _ _ i n i t _ _ ( s e l f , r e s u l t , f a i l = F a l s e ) : s e l f . r e s u l t = r e s u l t s e l f . f a i l = f a i l c l a s s F a i l ( R e s u l t ) : d e f _ _ i n i t _ _ ( s e l f , r e a s o n = T r u e ) : R e s u l t . _ _ i n i t _ _ ( s e l f , N o n e , r e a s o n ) d e f f ( n ) : i f n > 1 0 : r e t u r n R e s u l t ( n * 1 0 ) e l s e : r e t u r n F a i l ( " % d i s t o o s m a l l " % n ) d e f t e s t ( n ) : r = f ( n ) i f r . f a i l : p r i n t r . f a i l e l s e : p r i n t r . r e s u l t d e f t e s t 2 ( n ) : r = f ( n ) i f i s i n s t a n c e ( r , F a i l ) : # C o u l d a l s o d o t h i s p r i n t r . f a i l e l s e : p r i n t r . r e s u l t t e s t ( 9 ) t e s t ( 1 1 ) t e s t 2 ( 9 ) t e s t 2 ( 1 1 )
l a s s R e s u l t ( o b j e c t ) : d e f _ _ i n i t _ _ ( s e l f , r e s u l t , f a i l = F a l s e ) : s e l f . _ _ r e s u l t = r e s u l t s e l f . f a i l = f a i l @ p r o p e r t y d e f r e s u l t ( s e l f ) : # G e t t e r i f s e l f . _ _ r e s u l t a n d s e l f . f a i l = = F a l s e : r e t u r n s e l f . _ _ r e s u l t e l s e : r a i s e E x c e p t i o n ( " A c c e s s e d R e s u l t . r e s u l t d u r i n g f a i l u r e " , s e l f . f a i l ) c l a s s F a i l ( R e s u l t ) : d e f _ _ i n i t _ _ ( s e l f , r e a s o n = T r u e ) : R e s u l t . _ _ i n i t _ _ ( s e l f , N o n e , r e a s o n ) d e f f ( n ) : i f n > 1 0 : r e t u r n R e s u l t ( n * 1 0 ) e l s e : r e t u r n F a i l ( " % d i s t o o s m a l l " % n ) d e f t e s t ( n ) : r = f ( n ) i f r . f a i l : p r i n t r . f a i l e l s e : p r i n t r . r e s u l t t e s t ( 9 ) t e s t ( 1 1 ) f ( 9 ) . r e s u l t
r e s u l t . p y c l a s s R e s u l t E r r o r ( E x c e p t i o n ) : d e f _ _ i n i t _ _ ( s e l f , r e a s o n ) : E x c e p t i o n . _ _ i n i t _ _ ( s e l f , " A c c e s s e d R e s u l t . r e s u l t d u r i n g f a i l u r e " , r e a s o n ) c l a s s R e s u l t ( o b j e c t ) : d e f _ _ i n i t _ _ ( s e l f , r e s u l t , f a i l = F a l s e ) : s e l f . _ _ r e s u l t = r e s u l t s e l f . f a i l = f a i l @ p r o p e r t y # G e t t e r d e f r e s u l t ( s e l f ) : i f s e l f . _ _ r e s u l t a n d s e l f . f a i l = = F a l s e : r e t u r n s e l f . _ _ r e s u l t e l s e : r a i s e R e s u l t E r r o r ( s e l f . f a i l ) c l a s s F a i l ( R e s u l t ) : d e f _ _ i n i t _ _ ( s e l f , r e a s o n = T r u e ) : R e s u l t . _ _ i n i t _ _ ( s e l f , N o n e , r e a s o n )
o m r e s u l t i m p o r t * d e f f ( n ) : i f n > 1 0 : r e t u r n R e s u l t ( n * 1 0 ) e l s e : r e t u r n F a i l ( " % d i s t o o s m a l l " % n ) d e f t e s t ( n ) : r = f ( n ) i f r . f a i l : p r i n t r . f a i l e l s e : p r i n t r . r e s u l t t e s t ( 9 ) t e s t ( 1 1 ) f ( 9 ) . r e s u l t Can we get rid of the ifelse using functional? m a p ( p r i n t , f ( n ) ) Monads for Python: http://www.infoq.com/presentations/MonadsCode