cv.cm

Pemrograman · Bahasa pemrograman

Tutorial Erlang untuk pemula

7 menit baca

Jawaban singkat

Erlang adalah bahasa fungsional dan konkuren yang dibuat di Ericsson untuk sistem yang harus terus menyala, seperti sentral telepon dan server pesan. Program berjalan di virtual machine BEAM sebagai banyak proses ringan yang berkomunikasi lewat pesan dan di-restart oleh supervisor ketika gagal. Halaman ini membahas cara mengompilasi modul, tipe, pattern matching, rekursi, list dan map, proses, OTP, dan penanganan error.

Apa itu Erlang dan untuk apa dipakai

Erlang adalah bahasa fungsional bertipe dinamis yang dirancang di Ericsson pada 1980-an untuk sentral telepon yang harus berjalan tanpa henti. Erlang dirilis sebagai open source pada 1998. Program Erlang berjalan di virtual machine BEAM sebagai banyak proses ringan yang tidak berbagi apa pun dan berkomunikasi lewat pesan, dan disertai OTP (Open Telecom Platform), kumpulan library dan prinsip desain untuk membangun sistem yang tahan gangguan.

Erlang dipakai di perangkat telekomunikasi, platform pesan (backend WhatsApp terkenal dibangun dengan Erlang), message broker seperti RabbitMQ, database seperti Riak dan CouchDB, serta sistem pembayaran dan IoT. Elixir dan Gleam juga berjalan di BEAM dan dapat memakai library Erlang secara langsung.

Memasang Erlang dan menjalankan Hello, world

Pasang Erlang/OTP dari erlang.org atau dengan package manager (misalnya brew install erlang atau apt install erlang). Kode berada di dalam modul; setiap file name.erl mendefinisikan modul name dan mendaftar fungsi yang diekspornya.

% hello.erl
-module(hello).
-export([main/0]).

main() ->
    io:format("Hello, world!~n").
erlc hello.erl                          # compiles to hello.beam
erl -noshell -s hello main -s init stop
# Hello, world!
erl                                     # interactive shell; c(hello). then hello:main().

Komentar diawali dengan %. ~n dalam format string adalah newline dan ~p mencetak term apa pun. Di shell, setiap ekspresi diakhiri dengan titik.

Tanda baca, variabel, dan tipe dasar

Tanda baca Erlang adalah kejutan terbesar bagi pemula: koma memisahkan ekspresi, titik koma memisahkan clause, dan titik mengakhiri sebuah fungsi atau perintah shell. Variabel diawali huruf kapital atau garis bawah dan hanya dapat diikat sekali.

1> Name = "Erlang".
"Erlang"
2> Year = 1986, Next = Year + 1.
1987
3> Year = 2000.
** exception error: no match of right hand side value 2000
4> 10 / 4.
2.5
5> {10 div 4, 10 rem 4}.
{2,2}
6> is_atom(ok).
true
7> <<"binary string">>.
<<"binary string">>
TipeContohCatatan
integer42, 16#FFpresisi arbitrer; 16# untuk heksadesimal
float3.14presisi ganda 64-bit
atomok, error, 'Hello'konstanta bernama, huruf kecil atau diberi kutip
tuple{ok, 42}kelompok nilai berukuran tetap
list[1, 2, 3]linked list; string seperti "abc" adalah list berisi integer
map#{name => "Ann"}penyimpanan key-value
binary<<"text">>urutan byte, cara efisien untuk menyimpan teks

true dan false adalah atom, bukan tipe tersendiri. = adalah match, bukan assignment, itulah sebabnya baris 3 gagal: Year sudah terikat ke 1986.

Pattern matching, case, dan guard

Pattern matching membongkar tuple, list, dan map serta memilih clause mana yang dijalankan. Guard, yang ditulis dengan when, menambahkan kondisi pada sebuah clause. case dan if adalah ekspresi yang mengembalikan nilai.

-module(match).
-export([describe/1, grade/1, demo/0]).

describe({ok, Value}) -> io_lib:format("ok: ~p", [Value]);
describe({error, Reason}) -> io_lib:format("error: ~p", [Reason]);
describe(_Other) -> "unknown".

grade(Score) when Score >= 90 -> a;
grade(Score) when Score >= 70 -> b;
grade(_) -> c.

demo() ->
    [First | Rest] = [1, 2, 3],
    #{name := Name} = #{name => "Ann", age => 31},
    Size = case length(Rest) of
               0 -> empty;
               N when N < 5 -> small;
               _ -> large
           end,
    io:format("~p ~p ~s ~p ~p~n", [First, Rest, Name, Size, grade(75)]),
    io:format("~s~n", [describe({error, timeout})]).
% match:demo() prints:
% 1 [2,3] Ann small b
% error: timeout

Setiap clause kecuali yang terakhir diakhiri dengan ;, dan yang terakhir diakhiri dengan .. Jika tidak ada clause yang cocok, proses memunculkan error function_clause atau case_clause.

Fungsi, rekursi, dan fungsi tingkat tinggi

Fungsi dikenali dari nama dan arity, ditulis name/Arity. Tidak ada perulangan; pengulangan memakai rekursi, dan fungsi tail-recursive berjalan dengan ruang stack konstan. Fungsi anonim ditulis fun ... end.

-module(fns).
-export([factorial/1, sum/1, demo/0]).

factorial(0) -> 1;
factorial(N) when N > 0 -> N * factorial(N - 1).

sum(List) -> sum(List, 0).          % public wrapper
sum([], Acc) -> Acc;                 % tail-recursive helper sum/2
sum([H | T], Acc) -> sum(T, Acc + H).

demo() ->
    Double = fun(X) -> X * 2 end,
    io:format("~p~n", [factorial(20)]),                    % 2432902008176640000
    io:format("~p~n", [sum([1, 2, 3, 4])]),                % 10
    io:format("~p~n", [Double(21)]),                       % 42
    io:format("~p~n", [lists:map(Double, [1, 2, 3])]),     % [2,4,6]
    io:format("~p~n", [[X * X || X <- [1, 2, 3, 4, 5], X rem 2 =:= 1]]). % [1,9,25]

Baris terakhir memakai list comprehension. Perhatikan operator pembanding: =:= dan =/= menguji kesamaan dan ketidaksamaan yang tepat, == dan /= membandingkan angka berdasarkan nilai (jadi 1 == 1.0 bernilai true), dan kurang dari atau sama dengan ditulis =<, bukan <=.

List, map, dan standard library

Modul lists menyediakan map, filter, foldl, sort, reverse, dan banyak lagi; maps bekerja dengan map; string dan binary menangani teks. Referensi lengkapnya ada di dokumentasi OTP.

1> L = [5, 3, 8, 1].
[5,3,8,1]
2> lists:sort(L).
[1,3,5,8]
3> lists:filter(fun(X) -> X > 2 end, L).
[5,3,8]
4> lists:foldl(fun(X, Acc) -> X + Acc end, 0, L).
17
5> M = #{ann => 31, bo => 25}, map_size(M).
2
6> M2 = M#{cy => 40}, map_size(M2).
3
7> maps:get(bo, M2).
25
8> maps:get(zed, M2, none).
none

M#{cy => 40} membuat map baru; M sendiri tidak pernah berubah. Gunakan := alih-alih => untuk memperbarui key yang harus sudah ada.

Proses dan pengiriman pesan

Proses adalah fitur khas Erlang. spawn memulai proses baru, ! mengirim pesan, dan receive menunggu satu pesan sambil mencocokkannya dengan pattern. Proses itu murah, sehingga sebuah sistem dapat menjalankan sangat banyak proses. Link dan monitor memungkinkan satu proses mengetahui ketika proses lain mati, dan supervisor OTP memanfaatkannya untuk me-restart worker yang gagal secara otomatis.

-module(counter).
-export([start/0, loop/1]).

start() ->
    Pid = spawn(counter, loop, [0]),
    Pid ! {add, 5},
    Pid ! {add, 10},
    Pid ! {get, self()},
    receive
        {count, N} -> io:format("count = ~p~n", [N])   % count = 15
    after 1000 ->
        io:format("timeout~n")
    end.

loop(Count) ->
    receive
        {add, N} -> loop(Count + N);
        {get, From} -> From ! {count, Count}, loop(Count)
    end.

Loop tulisan tangan ini persis yang digeneralisasi oleh behaviour gen_server dari OTP. Pada kode produksi Anda akan menulis modul gen_server dan menempatkannya di bawah supervisor.

Penanganan error dan "let it crash"

Erlang punya tiga kelas exception: error, throw, dan exit. Kegagalan yang sudah diperkirakan biasanya dikembalikan sebagai tuple {ok, Value} atau {error, Reason}. Yang tidak terduga sering tidak ditangkap sama sekali: proses crash, dan supervisor-nya me-restart proses itu ke kondisi awal yang baik. Bila memang perlu menangkap, gunakan try ... catch.

1> file:read_file("missing.txt").
{error,enoent}
2> try 1 / 0 catch error:badarith -> division_failed end.
division_failed
3> try throw(my_reason) catch throw:R -> {caught, R} end.
{caught,my_reason}
4> case file:read_file("missing.txt") of {ok, Bin} -> Bin; {error, Why} -> Why end.
enoent

Program lengkap kecil: frekuensi kata

Modul ini menghitung kata dalam sebuah string dengan map, mengurutkannya berdasarkan jumlah, dan mencetak tiga yang paling sering muncul. Modul ini menggabungkan fungsi string, lists:foldl, map, dan pengurutan kustom.

-module(wordfreq).
-export([top/2, main/0]).

top(Text, N) ->
    Words = string:lexemes(string:lowercase(Text), " .,;:!?\n"),
    Counts = lists:foldl(fun(W, Acc) ->
                                 maps:update_with(W, fun(C) -> C + 1 end, 1, Acc)
                         end, #{}, Words),
    Sorted = lists:sort(fun({W1, C1}, {W2, C2}) ->
                                {-C1, W1} =< {-C2, W2}
                        end, maps:to_list(Counts)),
    lists:sublist(Sorted, N).

main() ->
    Text = "The cat and the dog. The dog sat, and the cat ran.",
    [io:format("~s: ~p~n", [W, C]) || {W, C} <- top(Text, 3)],
    ok.
% the: 4
% and: 2
% cat: 2

Langkah selanjutnya

  • Dokumentasi Erlang/OTP di erlang.org, termasuk panduan Getting Started dan OTP Design Principles.
  • Learn You Some Erlang for Great Good!, buku online gratis.
  • rebar3, build tool standar, dan Hex, package manager yang dipakai bersama Elixir.
  • Halaman referensi gen_server dan supervisor setelah Anda membangun layanan sungguhan.

FAQ

Apakah Erlang masih dipakai?

Ya. Erlang masih menjalankan sistem telekomunikasi, layanan pesan, dan RabbitMQ, dan ekosistem BEAM terus berkembang lewat Elixir dan Gleam, yang dapat memakai library Erlang.

Apa itu OTP di Erlang?

OTP adalah kumpulan library, tool, dan prinsip desain yang disertakan bersama Erlang. OTP menyediakan behaviour seperti gen_server, supervisor, dan application yang menstrukturkan program tahan gangguan.

Sebaiknya belajar Erlang atau Elixir?

Elixir punya sintaks yang lebih ramah dan ekosistem web yang lebih besar, sedangkan Erlang lebih kecil dan merupakan bahasa tempat OTP serta banyak library inti ditulis. Keduanya berjalan di VM yang sama, dan menguasai salah satunya membuat yang lain mudah dibaca.

Apa arti let it crash?

Alih-alih menangani setiap kemungkinan error di dalam proses, Anda membiarkan proses gagal dan mengandalkan supervisor untuk me-restart-nya dalam kondisi bersih. Kode penanganan error tetap kecil dan sistem pulih secara otomatis.

Mengapa variabel Erlang diawali huruf kapital?

Erlang memakai huruf kapital untuk membedakan variabel dari atom: Name adalah variabel, sedangkan name adalah atom, yaitu nilai konstan.

Semua tutorial pemrograman