有没有办法告诉函数是否有匹配的子句而不在Erlang中调用它?

时间:2015-02-27 19:16:10

标签: erlang

E.g。假设我有一个名为caller的模块,其中定义的函数之一包含以下表达式:

Callee:some_function(foo, Bar)

调用者可能会尝试捕获function_clause,但调用者如何知道它直接来自Callee:some_function而不是其他一些函数调用(例如Callee:some_function本身的调用)?

2 个答案:

答案 0 :(得分:4)

您可以使用try-catch捕获function_clause错误,并检查堆栈跟踪是否匹配:

-module(foo).

-compile(export_all).

maybe_apply(Mod, Fun, Args) ->
    try apply(Mod, Fun, Args)
    catch
        error:function_clause ->
            case erlang:get_stacktrace() of
                [{Mod, Fun, Args} | _] ->
                    {error, function_clause};
                [{Mod, Fun, Args, _LineNumber} | _] ->
                    {error, function_clause};
                Stacktrace ->
                    {error, other_function_clause, Stacktrace}
            end
    end.

这是一个示例,说明如何区分lists:filter本身的函数子句错误和lists:filter调用的函数中的函数子句:

> foo:maybe_apply(lists, filter, [x, [1,2,3]]).
{error,function_clause}
> foo:maybe_apply(lists, filter, [fun(x) -> true end, [1,2,3]]).
{error,other_function_clause,
       [{erl_eval,'-inside-an-interpreted-fun-',[1],[]},
        {erl_eval,expr,3,[]}]}

答案 1 :(得分:1)

您可以使用catch (callee:some_function(foo, Bar))并分析错误消息(如果有):

1> catch (lists:filter(5,[1,2,3])).
{'EXIT',{function_clause,[{lists,filter,
                                 [5,[1,2,3]],
                                 [{file,"lists.erl"},{line,1283}]},
                          {erl_eval,do_apply,6,
                                    [{file,"erl_eval.erl"},{line,661}]},
                          {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,434}]},
                          {shell,exprs,7,[{file,"shell.erl"},{line,684}]},
                          {shell,eval_exprs,7,[{file,"shell.erl"},{line,639}]},
                          {shell,eval_loop,3,
                                 [{file,"shell.erl"},{line,624}]}]}}
2> catch(lists:map(fun ({X,Y}) -> X + Y end, [{1,2},{3,4}])).
[3,7]
3> catch(lists:map(fun ({X,Y}) -> X + Y end, [{1,2},{3,4,5}])).
{'EXIT',{function_clause,[{erl_eval,'-inside-an-interpreted-fun-',
                                    [{3,4,5}],
                                    []},
                          {erl_eval,expr,3,[]}]}}
4> 
4> catch(lists:map(fun ({X,Y}) -> X / Y end, [{1,2},{3,0}])).
{'EXIT',{badarith,[{erlang,'/',[3,0],[]},
                   {lists,map,2,[{file,"lists.erl"},{line,1237}]},
                   {lists,map,2,[{file,"lists.erl"},{line,1237}]},
                   {erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,661}]},
                   {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,434}]},
                   {shell,exprs,7,[{file,"shell.erl"},{line,684}]},
                   {shell,eval_exprs,7,[{file,"shell.erl"},{line,639}]},
                   {shell,eval_loop,3,[{file,"shell.erl"},{line,624}]}]}}
5> 

您可以识别您正在寻找的案例,因为它具有以下形式:

{'EXIT',{function_clause,[{callee,some_function,
                                     [foo, Bar],
                                     [{file,"callee.erl"},{line,LineNumber}]}|Stack]}}