Nested Cases

From Erlang Community

(Difference between revisions)
Revision as of 04:41, 10 August 2008 (edit)
Ravindranathakila (Talk | contribs)

← Previous diff
Current revision (04:42, 10 August 2008) (edit) (undo)
Ravindranathakila (Talk | contribs)

 
Line 110: Line 110:
3: 5> test:function(divide,[5,2]). 3: 5> test:function(divide,[5,2]).
4: ** exception error: no case clause matching {oper,divide} 4: ** exception error: no case clause matching {oper,divide}
- 5: in function t:function/2+ 5: in function test:function/2
6: 6> test:function(multiply,[5,2,3]). 6: 6> test:function(multiply,[5,2,3]).
7: ** exception error: no case clause matching {val,[5,2,3]} 7: ** exception error: no case clause matching {val,[5,2,3]}
- 8: in function t:function/2+ 8: in function test:function/2
We've got much more clarity here as we KNOW where to find the atoms 'val' and 'oper'. We could use the atoms 'case1' and 'case2' if we would like to see which case caused the error explicitly. (Handling null cases is defensive) We've got much more clarity here as we KNOW where to find the atoms 'val' and 'oper'. We could use the atoms 'case1' and 'case2' if we would like to see which case caused the error explicitly. (Handling null cases is defensive)

Current revision

[edit] Author

Rudolph van Graan

[edit] Article

This article describes one of the Erlang best practices. It is particularly difficult to trace errors when a programmer used many nested case statements in a single function.

Consider the following code fragment that implements a basic operation table:

  1: function(Operation,Values) ->
  2:   case Operation of
  3:     null -> 
  4:       null;
  5:     multiply -> 
  6:       case Values of 
  7:         [A,B] when integer(A), 
  8: 	               integer(B) -> 
  9: 	      {ok,A*B}
10:       end
11:     end.

As you can see, it contains a nested case (line 6).If we execute the multiplication operation, the code performs as expected:

(rvg@davinci)5> test:function(multiply,[5,2]).
{ok,10} 

Now, let us try an invalid operation:

(rvg@davinci)6> test:function(divide,[5,2]).

=ERROR REPORT==== 25-Dec-2004::20:30:28 ===
Error in process <0.43.0> on node 'rvg@davinci' with exit value: 
{{case_clause,divide},[{test,function,2},{shell,exprs,6},{shell,eval_loop,3}]}

** exited: {{case_clause,divide},
            [{test,function,2},{shell,exprs,6},{shell,eval_loop,3}]} **

As expected, the shell crashed with a case_clause. Now, let us try the same with invalid multiplication parameters:

(rvg@davinci)7> test:function(multiply,[5,2,3]).

=ERROR REPORT==== 25-Dec-2004::20:33:05 ===
Error in process <0.47.0> on node 'rvg@davinci' with exit value:
 {{case_clause,[5,2,3]},[{test,function,2},{shell,exprs,6},{shell,eval_loop,3}]}

** exited: {{case_clause,[5,2,3]},
            [{test,function,2},{shell,exprs,6},{shell,eval_loop,3}]} **

As you can see, the reason is still {case_clause,...}. In complex code, you would not easily figure out which case caused the problem. Let us fix the code:

 1: function(Operation,Values) ->
 2:   case Operation of
 3:     null -> 
 4:       null;
 5:     multiply ->
 6:       do_multiply(Values)
 7:   end.
 8: 
 9: do_multiply([Value1,Value2]) when integer(Value1),
10:                                   integer(Value2)->
11:   {ok,Value1*Value2}.

If we pass an invalid operation:

(rvg@davinci)11> test:function(divide,[5,2]).

=ERROR REPORT==== 25-Dec-2004::20:37:54 ===
Error in process <0.60.0> on node 'rvg@davinci' with exit value: 
{{case_clause,divide},[{test,function,2},{shell,exprs,6},{shell,eval_loop,3}]} 

** exited: {{case_clause,divide},
            [{test,function,2},{shell,exprs,6},{shell,eval_loop,3}]} **

You can see that the case clause in function(...) cannot handle the divide atom. If we pass invalid parameters:

(rvg@davinci)12> test:function(multiply,[5,2,3]). 

=ERROR REPORT==== 25-Dec-2004::20:38:54 ===
Error in process <0.62.0> on node 'rvg@davinci' with exit value:
 {function_clause,[{test,do_multiply,5,2,3},{erl_eval,do_apply,5},{shell,exprs,6},{shell,eval_loop,3}]}

** exited: {function_clause,[{test,do_multiply,5,2,3},
                             {erl_eval,do_apply,5},
                             {shell,exprs,6},
                             {shell,eval_loop,3}]} **

It is clear that the problem is in do_multiply(...)


[edit] Better Solution:

  1: -module(test).
  2: -export([function/2]).
  3: function(Oper,Val)->
  4:	case {oper,Oper} of
  5:		{oper,null}->
  6:			null;
  7:		{oper,multiply}->
  8:			case {val,Val} of
  9:				{val,[A,B]} when integer(A), integer(B)
 10:					-> {ok,A*B}
 11:			end
 12:	end.


  1: 4> test:function(multiply,[5,2]).
  2: {ok,10}
  3: 5> test:function(divide,[5,2]).
  4: ** exception error: no case clause matching {oper,divide}
  5:      in function  test:function/2
  6: 6> test:function(multiply,[5,2,3]).
  7: ** exception error: no case clause matching {val,[5,2,3]}
  8:      in function  test:function/2

We've got much more clarity here as we KNOW where to find the atoms 'val' and 'oper'. We could use the atoms 'case1' and 'case2' if we would like to see which case caused the error explicitly. (Handling null cases is defensive)

Erlang/OTP Projects
Personal tools