views:

309

answers:

6

I have a function in which I have a series of individual case statements.

case ... of
     ...
end,

case ... of
     ...
end,

...

etc.

I want to return from the function immediately when a particular case condition occurs in one of the case statements - so that the next case statement is not checked, and the function just exits/returns. How do I do that?

+4  A: 

Erlang does not have a return operator. You will need to refactor your code into smaller functions.

Your original code has two case expressions chained with the comma operator. I presume you have some side effects in the first case expression that you want to preserve. Below, I'm using an imaginary return operator:

case ... of
  P1 -> return E1;
  P2 -> E2;
end,

case ... of
  ...
end

An expression like this can be converted to real Erlang code using small functions and pattern matching with something resembling this:

case1(P1, ...) -> E1;
case1(P2, ...) -> E2, case2(...).
case2(...) -> ...

Disclaimer: It's been 10 years since I've written Erlang code, so my syntax may be off.

Ville Laurikari
Please provide an example of doing so.
Adam Lindberg
Example added, I hope it helps.
Ville Laurikari
+2  A: 

In Erlang you just use the pattern matching to trigger the appropriate function. If you have too many clauses to cover and deal with I would also suggest to refactor the code a little bit.

markmywords
+6  A: 

I would suggest you refactor to harness the full power of Erlang and its pattern matching abilities.

There isn't a return operator. Also, a little known fact is you can do something like:

Return=case ... of

a case statement can have a "return" value.

jldupont
That is why it is called a case expression.
Christian
Yes, that's something quite worth knowing! thanks.
jeffreyveon
@jeffreyveon: does my answer satisfy your question?
jldupont
Yes, thank you, thanks for helping me get going with erlang :)
jeffreyveon
A: 

use catch/throw

The caller says:

X = (catch foo(A, B)).

then write

foo(A, B) ->
    case ... of
     ...throw(X) ..
    end,

    case ... of
     ... throw (Y)
    end,
    ...

This is generally considered poor programming practice - since the program has multiple exit points and is difficult to grock

ja
I have a much better idea why it would be considered bad programming: You're abusing an error propagation mechanism for normal control flow. Instead, as the others have pointed out, use pattern matching.
harms
Throw in Erlang is specifically for non-local returns, not error handling - so in principle this is a valid approach. In practice I suspect that refactoring to avoid it would be better here, but without seeing the code it's hard to know.
cthulahoops
In principle it might be a valid approach, and it is indeed for non-local returns, but it is still bad practice to use it widely in your code. Using non-local returns makes it very hard for other programmers to understand the code, and makes it harder to debug. While it is not designed specifically for error handling, error handling is generally the only place you should use it. Additionally, if you are coding for error handling, you're not following a core Erlang philosophy, which is "code for success" : you're thinking more like an imperative programmer.
Tim
+6  A: 

Pattern matching is a good way to refactor a case statement - you can do something like this

testcase(1, X, Y) -> .... passed1;
testcase(2, X, Y) -> .... passed2;
testcase(N, X, Y) when N > 2 -> .... passedlarge;
testcase(_, X, Y) -> defaultcase.

and then your case statement simply wraps up to:

X = testcase(Number, Param1, Param2).

(X will be either passed1, passed2, passedlarge or defaultcase in this contrived example)

Alan Moore
Ok, looks awesome, thanks!
jeffreyveon
+1  A: 

One way is to cascade your case statements:

my_fun(X) ->
  case cond1(X) of
    true -> ret1;
    _ ->
      case cond2(X) of
        true -> ret2;
        _ ->
          ...
      end
  end.

Another one is to separate your case statements into clauses:

my_fun(X) ->
  my_fun(cond1, X).

my_fun(cond1, X) ->
  case cond1(X) of
    true -> ret1;
    _    -> my_fun(cond2, X)
  end;

my_fun(cond2, X) ->
  case cond2(X) of
    true -> ret2;
    _    -> my_fun(cond3, X)
  end;

...
Zed