Know your library mini-series. How to use Scala library classes: Option, Either and Try. Functional exception handling, i.e. what to do with corner cases when they arise.
r i n g f o o = r e q u e s t . p a r a m s ( " f o o " ) i f ( f o o ! = n u l l ) { S t r i n g b a r = r e q u e s t . p a r a m s ( " b a r " ) i f ( b a r ! = n u l l ) { d o S o m e t h i n g ( f o o , b a r ) } e l s e { t h r o w n e w A p p l i c a t i o n E x c e p t i o n ( " B a r n o t f o u n d " ) } } e l s e { t h r o w n e w A p p l i c a t i o n E x c e p t i o n ( " F o o n o t f o u n d " ) }
* 1 . N o b o d y k n o w s a b o u t n u l l , n o t e v e n c o m p i l e r * / S t r i n g f o o = r e q u e s t . p a r a m s ( " f o o " ) / * 2 . A n n o y i n g c h e c k i n g * / i f ( f o o ! = n u l l ) { S t r i n g b a r = r e q u e s t . p a r a m s ( " b a r " ) / / i f ( b a r ! = n u l l ) { / * 3 . D a n g e r o f i n f a m o u s N u l l P o i n t e r E x c e p t i o n , e v e r b o d y c a n f o r g e t s o m e c h e c k * / d o S o m e t h i n g ( f o o , b a r ) / / } e l s e { / * 4 . O p t i o n a t e d d e t a i l e d f a i l u r e s , s o m e t i m e s f a i l u r e i n t h e e n d i s e n o u g h * / / / t h r o w n e w A p p l i c a t i o n E x c e p t i o n ( " B a r n o t f o u n d " ) / / } } e l s e { / * 5 . D e s i g n f l a w , j u s t o r i g i n a l e x c e p t i o n r e p l a c e m e n t * / t h r o w n e w A p p l i c a t i o n E x c e p t i o n ( " F o o n o t f o u n d " ) }
n u l l Groovy provides null-safe operator for accessing properties Clojure uses n i l which is okay very often, but sometimes it leads to an exception higher in call hierarchy f o o ? . b a r ? . b a z
or none element s e a l e d a b s t r a c t c l a s s O p t i o n [ A ] c a s e c l a s s S o m e [ + A ] ( x : A ) e x t e n d s O p t i o n [ A ] c a s e o b j e c t N o n e e x t e n d s O p t i o n [ N o t h i n g ]
present on type level 2. You are forced by the compiler to deal with it 3. No way to accidentally rely on presence of a value 4. Clearly documents an intention
method on companion object v a l c e r t a i n = S o m e ( " S u n c o m e s u p " ) v a l p i t t y = N o n e v a l n o n S e n s e = S o m e ( n u l l ) v a l m u c h B e t t e r = O p t i o n ( n u l l ) / / R e s u l t s t o N o n e v a l c e r t a i n A g a i n = O p t i o n ( " S u n c o m e s u p " ) / / S o m e ( S u n c o m e s u p )
(only in exceptional cases) / / A s s u m e t h a t d e f p a r a m [ S t r i n g ] ( n a m e : S t r i n g ) : O p t i o n [ S t r i n g ] . . . v a l f o o P a r a m = r e q u e s t . p a r a m ( " f o o " ) v a l f o o = i f ( f o o P a r a m . i s D e f i n e d ) { f o o P a r a m . g e t / / t h r o w s N o S u c h E l e m e n t E x c e p t i o n w h e n N o n e } e l s e { " D e f a u l t f o o " / / D e f a u l t v a l u e }
v a l f o o = r e q u e s t . p a r a m ( " f o o " ) m a t c h { c a s e S o m e ( v a l u e ) = > v a l u e c a s e N o n e = > " D e f a u l t f o o " / / D e f a u l t v a l u e }
It's evaluated lazily. / / a n y l o n g c o m p u t a t i o n f o r d e f a u l t v a l u e v a l f o o = r e q u e s t . p a r a m ( " f o o " ) g e t O r E l s e ( " D e f a u l t f o o " )
e c l a s s U s e r ( i d : I n t , n a m e : S t r i n g , a g e : O p t i o n [ I n t ] ) / / I n d o m a i n m o d e l , a n y o p t i o n a l v a l u e h a s t o b e e x p r e s s e d w i t h O p t i o n o b j e c t U s e r D a o { d e f f i n d B y I d ( i d : I n t ) : O p t i o n [ U s e r ] = . . . / / I d c a n a l w a y s b e i n c o r r e c t , e . g . i t ' s p o s s i b l e t h a t u s e r d o e s n o t e x i s t a l r e a d y }
S u p p o s e w e h a v e a n u s e r I d f r o m s o m e w h e r e v a l u s e r O p t = U s e r D a o . f i n d B y I d ( u s e r I d ) / / J u s t p r i n t u s e r n a m e u s e r O p t . f o r e a c h { u s e r = > p r i n t l n ( u s e r . n a m e ) / / N o t h i n g w i l l b e p r i n t e d w h e n N o n e } / / R e s u l t i s U n i t ( l i k e v o i d i n J a v a ) / / O r m o r e c o n c i s e u s e r O p t . f o r e a c h ( u s e r = > p r i n t l n ( u s e r ) ) / / O r e v e n m o r e u s e r O p t . f o r e a c h ( p r i n t l n ( _ ) ) u s e r O p t . f o r e a c h ( p r i n t l n )
/ E x t r a c t i n g a g e v a l a g e O p t = U s e r D a o . f i n d B y I d ( u s e r I d ) . m a p ( _ . a g e ) / / R e t u r n s O p t i o n [ O p t i o n [ I n t ] ] v a l a g e O p t = U s e r D a o . f i n d B y I d ( u s e r I d ) . m a p ( _ . a g e . m a p ( a g e = > a g e ) ) / / R e t u r n s O p t i o n [ O p t i o n [ I n t ] ] t o o / / E x t r a c t i n g a g e , t a k e 2 v a l a g e O p t = U s e r D a o . f i n d B y I d ( u s e r I d ) . f l a t M a p ( _ . a g e . m a p ( a g e = > a g e ) ) / / R e t u r n s O p t i o n [ I n t ]
left side of generator / / E x t r a c t i n g a g e , t a k e 3 v a l a g e O p t = f o r { u s e r < - U s e r D a o . f i n d B y I d ( u s e r I d ) a g e < - u s e r . a g e } y i e l d a g e / / R e t u r n s O p t i o n [ I n t ] / / E x t r a c t i n g a g e , t a k e 3 v a l a g e O p t = f o r { U s e r ( _ , S o m e ( a g e ) ) < - U s e r D a o . f i n d B y I d ( u s e r I d ) } y i e l d a g e / / R e t u r n s O p t i o n [ I n t ]
notation Both prints Rule of thumb: wrap all mandatory fields with Option and then concatenate with optional ones d e f p r e t t y P r i n t ( u s e r : U s e r ) = L i s t ( O p t i o n ( u s e r . n a m e ) , u s e r . a g e ) . m k S t r i n g ( " , " ) d e f p r e t t y P r i n t ( u s e r : U s e r ) = ( O p t i o n ( u s e r . n a m e ) + + u s e r . a g e ) . m k S t r i n g ( " , " ) v a l f o o = U s e r ( " F o o " , S o m e ( 1 0 ) ) v a l b a r = U s e r ( " B a r " , N o n e ) p r e t t y P r i n t ( f o o ) / / P r i n t s " F o o , 1 0 " p r e t t y P r i n t ( b a r ) / / P r i n t s " B a r "
appropriate, when U s e r is desired directly o b j e c t U s e r D a o { / / N e w m e t h o d d e f c r e a t e U s e r : U s e r } v a l u s e r O p t = U s e r D a o . f i n d B y I d ( u s e r I d ) o r E l s e S o m e ( U s e r D a o . c r e a t e ) v a l u s e r = U s e r D a o . f i n d B y I d ( u s e r I d ) g e t O r E l s e U s e r D a o . c r e a t e
a b s t r a c t c l a s s O p t i o n [ A ] { d e f f o l d [ B ] ( i f E m p t y : Ó B ) ( f : ( A ) Ó B ) : B d e f f i l t e r ( p : ( A ) Ó B o o l e a n ) : O p t i o n [ A ] d e f e x i s t s ( p : ( A ) Ó B o o l e a n ) : B o o l e a n . . . }
went wrong, cause is lost forever c a s e c l a s s U s e r F i l t e r ( n a m e : S t r i n g , a g e : I n t ) d e f p a r s e F i l t e r ( i n p u t : S t r i n g ) : O p t i o n [ U s e r F i l t e r ] = { f o r { n a m e < - p a r s e N a m e ( i n p u t ) a g e < - p a r s e A g e ( i n p u t ) } y i e l d U s e r F i l t e r ( n a m e , a g e ) } / / S u p p o s e t h a t p a r s e N a m e a n d p a r s e A g e t h r o w s F i l t e r E x c e p t i o n d e f p a r s e F i l t e r ( i n p u t : S t r i n g ) : O p t i o n [ U s e r F i l t e r ] t h r o w s F i l t e r E x c e p t i o n { . . . } / / c a l l e r s i d e v a l f i l t e r = t r y { p a r s e F i l t e r ( i n p u t ) } c a t c h { c a s e e : F i l t e r E x c e p t i o n = > w h a t T o D o I n T h e M i d d l e O f T h e C o d e ( e ) }
e d a b s t r a c t c l a s s E i t h e r [ + L , + R ] c a s e c l a s s L e f t [ + L , + R ] ( a : L ) e x t e n d s E i t h e r [ L , R ] c a s e c l a s s R i g h t [ + L , + R ] ( b : R ) e x t e n d s E i t h e r [ L , R ]
t [ L ] or R i g h t [ R ] , but never both. 2. No explicit sematics, but by convention L e f t [ L ] represents corner case and R i g h t [ R ] desired one. 3. Functional way of dealing with alternatives, consider: 4. Again, it clearly documents an intention d e f d o S o m e t h i n g ( ) : I n t t h r o w s S o m e E x c e p t i o n / / w h a t i s t h i s s a y i n g ? t w o p o s s i b l e o u t c o m e s d e f d o S o m e t h i n g ( ) : E i t h e r [ S o m e E x c e p t i o n , I n t ] / / m o r e f u n c t i o n a l o n l y o n e r e t u r n v a l u e
e r ( . . . ) factory method on companion object. d e f p a r s e A g e ( i n p u t : S t r i n g ) : E i t h e r [ S t r i n g , I n t ] = { t r y { R i g h t ( i n p u t . t o I n t ) } c a t c h { c a s e n f e : N u m b e r F o r m a t E x c e p t i o n = > L e f t ( " U n a b l e t o p a r s e a g e " ) } }
in exceptional cases) d e f p a r s e F i l t e r ( i n p u t : S t r i n g ) : E i t h e r [ S t r i n g , E x t e n d e d F i l t e r ] = { v a l n a m e = p a r s e N a m e ( i n p u t ) i f ( n a m e . i s R i g h t ) { v a l a g e = p a r s e A g e ( i n p u t ) i f ( a g e . i s R i g h t ) { R i g h t ( U s e r F i l t e r ( t i m e , r a t i n g ) ) } e l s e a g e } e l s e n a m e }
d e f p a r s e F i l t e r ( i n p u t : S t r i n g ) : E i t h e r [ S t r i n g , E x t e n d e d F i l t e r ] = { p a r s e N a m e ( i n p u t ) m a t c h { c a s e R i g h t ( n a m e ) = > p a r s e A g e ( i n p u t ) m a t c h { c a s e R i g h t ( a g e ) = > U s e r F i l t e r ( n a m e , a g e ) c a s e e r r o r : L e f t [ _ ] = > e r r o r } c a s e e r r o r : L e f t [ _ ] = > e r r o r } }
t h e r as collection. It's unbiased, you have to define what is your prefered side. Working on success, only 1st error is returned. e i t h e r . r i g h t returns R i g h t P r o j e c t i o n d e f p a r s e F i l t e r ( i n p u t : S t r i n g ) : E i t h e r [ S t r i n g , U s e r F i l t e r ] = { f o r { n a m e < - p a r s e N a m e ( i n p u t ) . r i g h t a g e < - p a r s e A g e ( i n p u t ) . r i g h t } y i e l d R i g h t ( U s e r F i l t e r ( n a m e , a g e ) ) }
are collected. e i t h e r . l e f t returns L e f t P r o j e c t i o n d e f p a r s e F i l t e r ( i n p u t : S t r i n g ) : E i t h e r [ L i s t [ S t r i n g ] , U s e r F i l t e r ] = { v a l n a m e = p a r s e N a m e ( i n p u t ) v a l a g e = p a r s e A g e ( i n p u t ) v a l e r r o r s = n a m e . l e f t . t o O p t i o n + + a g e . l e f t . t o O p t i o n i f ( e r r o r s . i s E m p t y ) { R i g h t ( U s e r F i l t e r ( n a m e . r i g h t . g e t , a g e . r i g h t . g e t ) ) } e l s e { L e f t ( e r r o r s ) } }
E i t h e r You can use m a p , f l a t M a p on them too, but beware This is inconsistent in regdard to other collections. v a l r i g h t T h i n g = R i g h t ( U s e r ( " F o o " , S o m e ( 1 0 ) ) ) v a l p r o j e c t i o n = r i g h t T h i n g . r i g h t / / T y p e i s R i g h t P r o j e c t i o n [ U s e r ] v a l r i g h t T h i n g A g a i n = p r o j e c t i o n . m a p ( _ . n a m e ) / / I s n ' t R i g h t P r o j e c t i o n [ U s e r ] b u t R i g h t [ U s e r ]
for comprehensions. This won't compile. After removing syntactic suggar, we get We need projection again f o r { n a m e < - p a r s e N a m e ( i n p u t ) . r i g h t b i g N a m e < - n a m e . c a p i t a l i z e } y i e l d b i g N a m e p a r s e N a m e ( i n p u t ) . r i g h t . m a p { n a m e = > v a l b i g N a m e = n a m e . c a p i t a l i z e ( b i g N a m e ) } . m a p { c a s e ( x ) = > x } / / M a p i s n o t m e m b e r o f E i t h e r
r regardless if it's R i g h t or L e f t on the same type Accepts functions, both are evaluated lazily. Result from both functions has same type. / / O n c e u p o n a t i m e i n c o n t r o l l e r p a r s e F i l t e r ( i n p u t ) . f o l d ( / / B a d ( L e f t ) s i d e t r a n s f o r m a t i o n t o H t t p R e s p o n s e e r r o r s = > B a d R e q u e s t ( " E r r o r i n f i l t e r " ) / / G o o d ( R i g h t ) s i d e t r a n s f o r m a t i o n t o H t t p R e s p o n s e f i l t e r = > O k ( d o S o m e t h i n g W i t h ( f i l t e r ) ) )
a b s t r a c t c l a s s E i t h e r [ + A , + B ] { d e f j o i n L e f t [ A 1 > : A , B 1 > : B , C ] ( i m p l i c i t e v : < : < [ A 1 , E i t h e r [ C , B 1 ] ] ) : E i t h e r [ C , B 1 ] d e f j o i n R i g h t [ A 1 > : A , B 1 > : B , C ] ( i m p l i c i t e v : < : < [ B 1 , E i t h e r [ A 1 , C ] ] ) : E i t h e r [ A 1 , C ] d e f s w a p : P r o d u c t w i t h S e r i a l i z a b l e w i t h E i t h e r [ B , A ] }
can use classic t r y / c a t c h / f i n a l l y construct d e f p a r s e A g e ( i n p u t : S t r i n g ) : E i t h e r [ S t r i n g , I n t ] = { t r y { R i g h t ( i n p u t . t o I n t ) } c a t c h { c a s e n f e : N u m b e r F o r m a t E x c e p t i o n = > L e f t ( " U n a b l e t o p a r s e a g e " ) } }
2 But, it's t r y / c a t c h / f i n a l l y on steroids thanks to pattern matching t r y { s o m e H o r r i b l e C o d e H e r e ( ) } c a t c h { / / C a t c h i n g m u l t i p l e t y p e s c a s e e @ ( _ : I O E x c e p t i o n | _ : N a s t y E x p c e p t i o n ) = > c l e a n U p M e s s ( ) / / C a t c h i n g e x c e p t i o n s b y m e s s a g e c a s e e : A n o t h e r N a s t y E x c e p t i o n i f e . g e t M e s s a g e c o n t a i n s " W r o n g a g a i n " = > c l e a n U p M e s s ( ) / / C a t c h i n g a l l e x c e p t i o n s c a s e e : E x c e p t i o n = > c l e a n U p M e s s ( ) }
3 It's powerful, but beware Never do this! Prefered approach of catching all t r y { s o m e H o r r i b l e C o d e H e r e ( ) } c a t c h { / / T h i s w i l l m a t c h s c a l a . u t i l . c o n t r o l . C o n t r o l T h r o w a b l e t o o c a s e _ = > c l e a n U p M e s s ( ) } t r y { s o m e H o r r i b l e C o d e H e r e ( ) } c a t c h { / / T h i s w i l l m a t c h s c a l a . u t i l . c o n t r o l . C o n t r o l T h r o w a b l e t o o c a s e t : C o n t r o l T h r o w a b l e = > t h r o w t c a s e _ = > c l e a n U p M e s s ( ) }
a value the RHS can be replaced with? No. 2. Code base can become ugly 3. Exceptions do not go well with concurrency v a l s o m e t h i n g = t h r o w n e w I l l e g a l A r g u m e n t E x c e p t i o n ( " F o o i s m i s s i n g " ) / / R e s u l t t y p e i s N o t h i n g
r t s c a l a . u t i l . c o n t r o l . _ and Collection of T h r o w a b l e or value s e a l e d t r a i t T r y [ A ] c a s e c l a s s F a i l u r e [ A ] ( e : T h r o w a b l e ) e x t e n d s T r y [ A ] c a s e c l a s s S u c c e s s [ A ] ( v a l u e : A ) e x t e n d s T r y [ A ]
c e s s [ T ] or may be F a i l u r e [ T ] ending with T h r o w a b l e on type level 2. Similar to O p t i o n , it's S u c c e s s biased 3. It's t r y / c a t c h without boilerplate 4. Again it clearly documents what is happening
i o n are present s e a l e d a b s t r a c t c l a s s T r y [ + T ] { / / T h r o w s e x c e p t i o n o f F a i l u r e o r r e t u r n v a l u e o f S u c c e s s d e f g e t : T / / O l d w a y c h e c k s d e f i s F a i l u r e : B o o l e a n d e f i s S u c c e s s : B o o l e a n / / m a p , f l a t M a p & C o . d e f m a p [ U ] ( f : ( T ) Ó U ) : T r y [ U ] d e f f l a t M a p [ U ] ( f : ( T ) Ó T r y [ U ] ) : T r y [ U ] / / S i d e e f f e c t i n g d e f f o r e a c h [ U ] ( f : ( T ) Ó U ) : U n i t / / D e f a u l t v a l u e d e f g e t O r E l s e [ U > : T ] ( d e f a u l t : Ó U ) : U / / C h a i n i n g d e f o r E l s e [ U > : T ] ( d e f a u l t : Ó T r y [ U ] ) : T r y [ U ] }
Failure Converting to O p t i o n d e f p a r s e A g e ( i n p u t : S t r i n g ) : T r y [ I n t ] = T r y ( i n p u t . t o I n t ) v a l a g e = p a r s e A g e ( " n o t a n u m b e r " ) r e c o v e r { c a s e e : N u m b e r F o r m a t E x c e p t i o n = > 0 / / D e f a u l t v a l u e c a s e _ = > - 1 / / A n o t h e r d e f a u l t v a l u e } / / R e s u l t i s a l w a y s S u c c e s s v a l a g e O p t = a g e . t o O p t i o n / / W i l l b e S o m e i f S u c c e s s , N o n e i f F a i l u r e
h [ T ] c a t c h i n g ( c l a s s O f [ N u m b e r F o r m a t E x c e p t i o n ] ) { i n p u t . t o I n t } / / R e t u r n s C a t c h [ I n t ]
h e r Converting to T r y c a t c h i n g ( c l a s s O f [ N u m b e r F o r m a t E x c e p t i o n ] ) . o p t { i n p u t . t o I n t } / / R e t u r n s O p t i o n [ I n t ] f a i l i n g ( c l a s s O f [ N u m b e r F o r m a t E x c e p t i o n ] ) { i n p u t . t o I n t } / / R e t u r n s O p t i o n [ I n t ] c a t c h i n g ( c l a s s O f [ N u m b e r F o r m a t E x c e p t i o n ] ) . e i t h e r { i n p u t . t o I n t } / / R e t u r n s E i t h e r [ T h r o w a b l e , I n t ] c a t c h i n g ( c l a s s O f [ N u m b e r F o r m a t E x c e p t i o n ] ) . w i t h T r y { i n p u t . t o I n t } / / R e t u r n s T r y [ I n t ]
of: V i r t u a l M a c h i n e E r r o r , T h r e a d D e a t h , I n t e r r u p t e d E x c e p t i o n , L i n k a g e E r r o r , C o n t r o l T h r o w a b l e , N o t I m p l e m e n t e d E r r o r n o n F a t a l C a t c h { p r i n t l n ( i n p u t . t o I n t ) }
c a t c h i n g ( c l a s s O f [ N u m b e r F o r m a t E x c e p t i o n ] ) . a n d F i n a l l y { p r i n t l n ( " A g e p a r s e d s o m e h o w " ) } . a p p l y { i n p u t . t o I n t } u l t i m a t e l y ( p r i n t l n ( " A g e p a r s e d s o m e h o w " ) ) { i n p u t . t o I n t }