- erlang.org/community has a bunch of vital links for tooling, learning, libraries.
- https://erlangforums.com/ and https://stackoverflow.com/questions/tagged/erlang, for questions
- Lisp-Flavored Erlang is a lot of fun as well, with a lot of resources and a Discord.
- An editor. Yours probably has decent support for Erlang. Vim, Emacs, and VS Code should be fine
- Fossil and/or Git, for version control. You'll want git to publish to hex.pm
- Erlang itself from your package manager or the website, or follow asdf cheat sheet to manage multiple Erlang/OTP installs
- rebar3 for a build system and to easily use other tools such as formatters and linters
- hex.pm for libraries and your own published libraries
- erlperf for Erlang-vs.-Erlang benchmarks
- vim-erlang, because vim/nvim's shipped plugin hasn't updated in 10 years and lacks new syntax like
maybeexpressions.
erlperf example
Consider:
-module(shuffle).
-export([do/1, do2/1, do3/1, do4/1]).
do(List) ->
List2 = cut(List),
AccInit = {[], [], [], [], []},
{L1, L2, L3, L4, L5} = lists:foldl(
fun(E, Acc) ->
P = rand:uniform(5),
L = element(P, Acc),
setelement(P, Acc, [E | L])
end,
AccInit,
List2
),
lists:flatten([L1, L2, L3, L4, L5]).
cut(List) ->
Rand = rand:uniform(length(List)),
{A, B} = lists:split(Rand, List),
B ++ A.
do2(List) ->
[X || {_, X} <- lists:sort([{rand:uniform(), N} || N <- List])].
do3(L) ->
do3(list_to_tuple(L), length(L)).
do3(T, 0) ->
tuple_to_list(T);
do3(T, Len) ->
Rand = random:uniform(Len),
A = element(Len, T),
B = element(Rand, T),
T1 = setelement(Len, T, B),
T2 = setelement(Rand, T1, A),
do3(T2, Len - 1).
do4([]) -> [];
do4([Elem]) -> [Elem];
do4(List) -> do4(List, length(List), []).
do4([], 0, Result) ->
Result;
do4(List, Len, Result) ->
{Elem, Rest} = nth_rest(random:uniform(Len), List),
do4(Rest, Len - 1, [Elem|Result]).
nth_rest(N, List) -> nth_rest(N, List, []).
nth_rest(1, [E|List], Prefix) -> {E, Prefix ++ List};
nth_rest(N, [E|List], Prefix) -> nth_rest(N - 1, List, [E|Prefix]).
These alternate shuffles can be benchmarked like so:
$ erlc shuffle.erl $ erlperf 'shuffle:do(lists:seq(1,1000)).' 'shuffle:do2(lists:seq(1,1000)).' 'shuffle:do3(lists:seq(1,1000)).' 'shuffle:do4(lists:seq(1,1000)).' Code || QPS Time Rel shuffle:do(lists:seq(1,1000)). 1 11510 86909 ns 100% shuffle:do2(lists:seq(1,1000)). 1 4026 248 us 35% shuffle:do3(lists:seq(1,1000)). 1 1284 779 us 11% shuffle:do4(lists:seq(1,1000)). 1 443 2256 us 4%
erlperf doesn't track memory usage. Here's a minimal companion for that:
#! /usr/bin/env escript
main(Args) ->
Bins = [compile(A) || A <- Args],
Words = [run(B) || B <- Bins],
lists:foreach(
fun({A, [{_Pid, _Count, W}]}) ->
io:fwrite("~10B - ~ts~n", [W, A])
end,
lists:zip(Args, Words)
),
erlang:halt(0).
compile(Code) ->
Lines = ["-module(f).", "-export([f/0]).", "f() -> " ++ Code],
Tokens = [
begin
{ok, T, _} = erl_scan:string(Line),
T
end
|| Line <- Lines
],
Forms = [
begin
{ok, F} = erl_parse:parse_form(T),
F
end
|| T <- Tokens
],
case compile:forms(Forms, [no_spawn_compiler_process, binary, return]) of
{ok, _, Bin} -> Bin;
{ok, _, Bin, _Warnings} -> Bin
end.
run(Bin) ->
{module, f} = code:load_binary(f, f, Bin),
1 = erlang:trace_pattern({f, f, 0}, true, [call_memory]),
1 = erlang:trace(self(), true, [call, set_on_first_spawn]),
Self = self(),
spawn(fun() ->
f:f(),
Self ! done
end),
receive
done -> ok
end,
{call_memory, Words} = erlang:trace_info({f, f, 0}, call_memory),
Words.
As used:
$ memused 'shuffle:do(lists:seq(1,1000)).' 'shuffle:do2(lists:seq(1,1000)).' 'shuffle:do3(lists:seq(1,1000)).' 'shuffle:do4(lists:seq(1,1000)).'
25415 - shuffle:do(lists:seq(1,1000)).
36744 - shuffle:do2(lists:seq(1,1000)).
2020372 - shuffle:do3(lists:seq(1,1000)).
983250 - shuffle:do4(lists:seq(1,1000)).