Try/Cond

Ulf Wiger (AL/EAB) ulf.wiger@REDACTED
Thu Oct 23 13:41:37 CEST 2003


From: Francesco Cesarini [mailto:francesco@REDACTED]

>>  There is no way of getting a non-local return from a function if you
>>do not use catch and throw.
>>
>Of course, as was mentioned yesterday, the main usage is when handling 
>unreliable input in deeply nested functions, e.g. parsing. Other than 
>that, it makes the code very hard to understand and debug. That, because 
>the errors occur when they are used as a replacement to gotos, used 
>across modules, and not with extreme care.

Personally, I practically never use catch and throw (well, I may use 
catch every once in a while, but only related to exits, not throw.

Non-local return *is* possible without throw.
I've mentioned before a side project of mine, where I generate a recursive-
descent parser from a yecc grammar. There, I use a backtracking fun to
break out of a path as soon as I find that it doesn't match.

Example (simplified):

try_rule([Rule|Rs], Tokens, MatchF, BackF, Result, S) ->
   NewMatchF = 
      fun(X, Ts1, S1) ->
           %% so far so good, continue
           try_rule(Rs, Ts1, Match, Back, set_var(X,Result), S1)
      end,
   (S#state.module):Pattern(Tokens, NewMatchF, BackF, Result, S);
try_rule([], Tokens, MatchF, BackF, Result, S) ->
   %% no more rules to test -- we have a match.
   MatchF(Result, Tokens, S).


match_rules([], Tokens, BackF, Result, S) ->
   %% no branches matched -- backtrack.
   Back();
...
match_rules([{RulePrefix, Branches}|Rules], Tokens, BackF, Result, S)... ->
   NewBackF = 
      fun() ->
         %% wrong track -- try the next rule
         match_rule(Rules, Tokens, BackF, Result, S)
      end,
   NewMatchF = 
      fun(X, Ts1, S1) ->
         %% a match -- continue down this path.
         NewResult = set_var(X, Result),
         match_rules(Branches, Ts1, NewBackF, NewResult, S1)
      end,
   (S#state.module):RulePrefix(Tokens, NewMatchF, NewBackF, S).


This has the (big) advantage of avoiding deep recursion in the 
emulator. Logically, we're still doing the same thing, but this is 
more efficient (except, it would seem, in Hipe, for unknown reasons). 
Basically, for each step, you define two closures (or reuse the ones 
that were passed to you): one to call when we know that we have a 
match, and one to call when we know that we're on the wrong track.

One could of course argue that it's less intuitive than catch/throw.

/Uffe



More information about the erlang-questions mailing list