2021 年你需要知道的关于 Erlang 的一切
- admin
- 07 Jan 2024
今天,我们将看一个相当古老且有些古怪的东西。 你们大多数人可能没有注意到的语言。 虽然 Erlang 不像某些现代编程语言那样流行,但它安静地运行着 WhatsApp 和微信等每天为大量用户提供服务的应用程序。 在这篇文章中,我将告诉你关于这门语言的更多事情、它的历史,以及你是否应该考虑自己学习它。 ## 什么是 Erlang,它在哪里使用? Erl
Read More很久以前写过一篇The Asymmetry of ++的文章,感谢Fede Bergero的发现。 让我们在不对称列表中添加更多…
注意:本文基于 OTP23。 该版本之后,许多事情都得到了改进。
最初,我想把这篇文章写成一个故事(就像我通常做的那样),但我突然掌握了太多的信息。 所以,我选择从table、cheatsheet开始。
该列表的目的是显示lists模块中的不同高阶函数在输入错误时如何反应,特别是在它们期望函数的参数中。 这个错误的输入可能是……
在我们开始之前,现在这里揭示一半的问题。让我向您介绍第一个不对称:
1> lists:map(something_wrong, [1,2,3]). % A non-empty list
** exception error: bad function something_wrong
in function lists:map/2 (lists.erl, line 1243)
2> lists:map(something_wrong, []). % An empty list
** exception error: no function clause matching lists:map(something_wrong,[]) (lists.erl, line 1242)
这发生在您将在下面看到的所有函数中:如果您使用错误的第一个参数和一个空列表调用这些函数,您将收到 function_clause 错误。 如果你用根本不是列表的东西来调用它们,也会发生同样的情况。 如果您使用非空列表调用它们,那么……继续阅读;)
至少,当 list 参数为空列表时,lists 模块会持续引发 function_clause 错误。 这是一件好事。
当列表不为空时会发生什么? 为了弄清楚这一点,我使用下面这个代码进行试验:
-module(a_module).
-format #{inline_clause_bodies => true,
inline_items => all,
paper => 100}.
-export([run/0]).
run() ->
Arity2 =
[all, any, dropwhile, filter, filtermap, flatmap, foreach, map, partition, search, sort,
splitwith, takewhile, usort],
Arity3 = [foldl, foldr, keymap, mapfoldl, mapfoldr],
Result =
[[fun_name(F, 2) | run2(fun lists:F/2)] || F <- Arity2]
++ [[fun_name(F, 3) | run3(fun lists:F/3)] || F <- Arity3],
format(Result),
Result.
fun_name(Name, Arity) -> io_lib:format("~p/~p", [Name, Arity]).
run2(Function) ->
[test(fun() -> Function(not_a_function, sample_list()) end),
test(fun() -> Function(fun wrong_arity/0, sample_list()) end)].
run3(Function) ->
[test(fun() -> Function(not_a_function, 1, sample_list()) end),
test(fun() -> Function(fun wrong_arity/0, 1, sample_list()) end)].
test(Fun) ->
try
Fun()
catch
_:{Err, _} -> Err;
_:Err -> Err
end.
wrong_arity() -> "no list function expects a 0-arity fun as a parameter".
sample_list() -> [{this, "is for keymap"}].
format(Result) ->
io:format("Function|No Fun|Bad Fun
"),
io:format("--------|------|-------
"),
lists:foreach(fun(Row) -> io:format("`~s` | `~p` | `~p`
", Row) end, Result).
这些是我得到的结果……
lists 函数 | No Fun | Bad Fun |
---|---|---|
all/2 | badfun | badarity |
any/2 | badfun | badarity |
dropwhile/2 | badfun | badarity |
filter/2 | function_clause | function_clause |
filtermap/2 | badfun | badarity |
flatmap/2 | badfun | badarity |
foreach/2 | badfun | badarity |
map/2 | badfun | badarity |
partition/2 | badfun | badarity |
search/2 | badfun | badarity |
sort/2 | function_clause | function_clause |
splitwith/2 | function_clause | function_clause |
takewhile/2 | badfun | badarity |
usort/2 | function_clause | function_clause |
foldl/3 | badfun | badarity |
foldr/3 | function_clause | function_clause |
keymap/3 | badfun | badarity |
mapfoldl/3 | badfun | badarity |
mapfoldr/3 | function_clause | function_clause |
因此,如您所见,如果您根本不使用函数,大多数函数会引发 badfun 错误,如果使用错误的函数 arity 一般会引发 badarity 错误,但其中一些也会引发 function_clause 错误。
关联上下文后,这就是每个错误在shell中的描述……
10> lists:foldl(not_fun, 1, [a,b,c]).
** exception error: bad function not_fun
in function lists:foldl/3 (lists.erl, line 1267)
11> lists:foldl(fun() -> {wrong, arity} end, 1, [a,b,c]).
** exception error: interpreted function with arity 0 called with two arguments
in function lists:foldl/3 (lists.erl, line 1267)
12> lists:foldr(fun() -> {wrong, arity} end, 1, [a,b,c]).
** exception error: no function clause matching lists:foldr(#Fun<erl_eval.45.79398840>,1,[]) (lists.erl, line 1279)
in function lists:foldr/3 (lists.erl, line 1280)
至少从我的角度来看,lists:foldl/3 的错误描述比 list:foldr/3 更清楚。 正如我之前告诉你的,当列表为空时,所有函数看起来都像 lists:foldr/3(即,它们会在每个糟糕的情况下引发 function_clause)。
从语义上讲,这不是问题。 到目前为止,所有这些功能都没有针对我所展示的场景进行定义。 因此,我们不应该依赖它们在使用不正确的值调用它们时产生的错误。
但是,如果您是 Erlang 新手或者您正在控制台上尝试一些东西,那么您不太可能错误地调用上述函数之一。 在这种情况下,function_clause 可能会非常令人困惑,而 badfun 或 badarity 可以更轻松地引导您找到问题的解决方案。
此外,如果出于某种原因,您需要编写代码来捕获这些错误并且您不想捕获任何错误(仅是您需要的特定错误),您必须始终小心处理 function_clause 以防您的 输入是一个空列表。
换句话说,这是个问题。
%% @doc similar to lists:all/2 but if the function has the wrong
%% arity it just returns false.
-spec my_all(fun((...) -> boolean()), [X]) -> boolean().
my_all(Pred, List) ->
try lists:all(Pred, List)
catch
error:{badarity, _} -> false
end.
因为…
45> a_module:my_all(fun(X) -> true end, [a,b,c]).
true
46> a_module:my_all(fun() -> true end, [a,b,c]).
false
47> a_module:my_all(fun() -> true end, []).
** exception error: no function clause matching lists:all(#Fun<erl_eval.45.79398840>,[]) (lists.erl, line 1216)
in function a_module:my_all/2
所以我们不得不像这样来写
%% @doc similar to lists:all/2 but if the function has the wrong
%% arity it just returns false.
-spec my_all(fun((...) -> boolean()), [X]) -> boolean().
my_all(Pred, List) ->
try lists:all(Pred, List)
catch
error:{badarity, _} -> false
error:function_clause when List == [] -> false
end.
我认为理想的情况是上表中的所有函数都具有相同的错误行为:
基本上,行为类似于 lists:foldl/3 ,但不要将空列表视为特殊情况。
是的! 列表模块中的更改实际上并没有那么复杂。 以 lists:map/2 为例,我们“只”需要在此处添加这两个子句:
map(F, [H|T]) ->
[F(H)|map(F, T)];
map(F, []) when is_function(F, 1) -> [];
map(F, []) when is_function(F) -> error({badarity, {F, []}});
map(_, []) -> error(badfun).
我不知道……也许吧? 也许这是赌一下的好时机? 🙃
今天,我们将看一个相当古老且有些古怪的东西。 你们大多数人可能没有注意到的语言。 虽然 Erlang 不像某些现代编程语言那样流行,但它安静地运行着 WhatsApp 和微信等每天为大量用户提供服务的应用程序。 在这篇文章中,我将告诉你关于这门语言的更多事情、它的历史,以及你是否应该考虑自己学习它。 ## 什么是 Erlang,它在哪里使用? Erl
Read More这篇文章探讨了 Erlang/OTP 25 中基于类型的新优化,其中编译器将类型信息嵌入到 BEAM 文件中,以帮助JIT(即时编译器)生成更好的代码。 ## 两全其美 OTP 22 中引入的基于SSA的编译器处理步骤进行了复杂的类型分析,允许进行更多优化和更好的生成代码。然而,Erlang 编译器可以做什么样的优化是有限制的,因为 BEAM 文件必须
Read More自从Erlang 存在,就一直有让它更快的需求和野心。这篇博文是一堂历史课,概述了主要的 Erlang 实现以及如何尝试提高 Erlang 的性能。 ## Prolog 解释器 Erlang 的第一个版本是在 1986 年在 Prolog 中实现的。那个版本的 Erlang 对于创建真正的应用程序来说太慢了,但它对于找出Erlang语言的哪些功能有用,哪
Read More