Nested Cases
From Erlang Community
| Revision as of 08:09, 2 September 2006 (edit) Rvg (Talk | contribs) ← Previous diff |
Current revision (04:42, 10 August 2008) (edit) (undo) Ravindranathakila (Talk | contribs) |
||
| (6 intermediate revisions not shown.) | |||
| Line 1: | Line 1: | ||
| + | [[Category:Best Practices]] | ||
| + | [[Category:Articles]] | ||
| + | ==Author== | ||
| + | [[User:Rvg|Rudolph van Graan]] | ||
| + | |||
| + | ==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. | 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. | ||
| Line 81: | Line 87: | ||
| It is clear that the problem is in do_multiply(...) | It is clear that the problem is in do_multiply(...) | ||
| + | |||
| + | |||
| + | == 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) | ||
Current revision
[edit] Author
[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)

Digg It
Del.icio.us
Reddit
Facebook
Stumble Upon
Technorati

