为什么Erlang会在高阶函数存在的情况下放弃生成堆栈跟踪?

Maa*_*gon 4 erlang strict stack-trace lazy-evaluation

当出现问题时,Erlang会产生很好的堆栈跟踪,当程序员想弄清楚它出错的原因时,这很有用.在存在更高阶函数的情况下,产生堆栈跟踪的机制似乎不足.例如,比较下面的两个例子(我在代码的注释中添加了生成的堆栈跟踪).

我读过之前为延迟评估(例如在Haskell中)生成堆栈跟踪的困难.但是由于Erlang被严格评估,我原本期望得到更好的结果.

我的问题是:什么使高阶函数成为Erlang用于生成堆栈跟踪的机制的问题?还有其他已知技术会产生更好的结果吗?

  1 -module(test).
  2 -export([f/1,a/1]).
  3 
  4 % Auxilary function to print stack trace (by throwing an exception).
  5 
  6 s(X) when X < 0 -> 0.
  7 
  8 % Example 1: Stack trace in presence of a higher order function.
  9 %
 10 % > test:f(1).
 11 % ** exception error: no function clause matching test:s(1) (test.erl, line 6)
 12 %      in function  test:g/2 (test.erl, line 15)
 13 
 14 f(X)   -> h(fun g/2,X).
 15 g(X,Y) -> s(X) - Y.
 16 h(I,X) -> I(X,3).
 17 
 18 % Example 2: Stack trace for chain of 1st order function applications.
 19 % 
 20 % > test:a(1).           
 21 % ** exception error: no function clause matching test:s(1) (test.erl, line 6)
 22 %      in function  test:c/1 (test.erl, line 28)
 23 %      in call from test:b/1 (test.erl, line 27)
 24 %      in call from test:a/1 (test.erl, line 26)
 25 
 26 a(X) -> b(X) + 1.
 27 b(X) -> c(X) + 2.
 28 c(X) -> s(X) + 3.
Run Code Online (Sandbox Code Playgroud)

leg*_*cia 9

这不是使用高阶函数本身的结果,而是尾部调用优化的结果.如果函数在返回之前做的最后一件事是调用另一个函数,那么Erlang VM会优化掉堆栈帧,因为它不再需要了.(这就是为什么你可以在没有溢出堆栈的情况下进行递归的原因.)

在你的榜样,两者fh做尾调用,因此,他们的堆栈帧不在堆栈跟踪可见.在另一方面,a,bc不做尾调用,因为他们必须执行他们返回给调用之前调用的函数的结果的加法.