Pemrograman · Bahasa pemrograman
Tutorial Elixir untuk pemula
Jawaban singkat
Elixir adalah bahasa fungsional dinamis yang berjalan di virtual machine Erlang (BEAM), dirancang untuk sistem yang skalabel, tahan gangguan, dan konkuren. Gagasan intinya adalah data immutable, pattern matching, operator pipe, dan proses ringan yang berkomunikasi lewat pesan. Halaman ini membahas persiapan, tipe, pattern matching, alur kontrol, modul dan fungsi, koleksi, proses, dan penanganan error.
Buka Penguji regexApa itu Elixir dan untuk apa dipakai
Elixir adalah bahasa fungsional dinamis yang dibuat oleh José Valim dan pertama kali dirilis pada 2012. Elixir berjalan di BEAM, virtual machine milik Erlang, sehingga mewarisi proses ringan, ketahanan terhadap gangguan, dan dukungan sistem terdistribusi dari Erlang, sambil menambahkan sintaks yang ramah, macro, dan tooling modern.
Elixir dipakai untuk aplikasi web dan API dengan framework Phoenix, fitur real-time seperti chat dan dashboard langsung (Phoenix LiveView), sistem pesan, data pipeline, perangkat embedded dengan Nerves, dan machine learning dengan Nx. Perusahaan memilihnya untuk layanan yang harus menangani banyak koneksi bersamaan dan tetap berjalan ketika sebagian komponennya gagal.
Memasang Elixir dan menjalankan Hello, world
Pasang Elixir dari elixir-lang.org (yang juga memasang atau memerlukan Erlang/OTP). Script memakai ekstensi .exs; file proyek yang dikompilasi memakai .ex. iex memulai shell interaktif.
# hello.exs
IO.puts("Hello, world!")elixir hello.exs
# Hello, world!
iex # interactive shell; type h(Enum.map) for help
mix new my_app # create a new project with Mix, the build toolKomentar diawali dengan #. Tanda kurung di sekitar argumen fungsi bersifat opsional di banyak tempat, jadi IO.puts "hi" juga berfungsi.
Tipe dasar dan immutability
Semua data di Elixir bersifat immutable. Mengikat ulang sebuah nama dengan = mengarahkannya ke nilai baru; nilai lama tidak pernah berubah. String adalah binary UTF-8 yang ditulis dengan tanda kutip ganda, dan atom adalah konstanta bernama yang diawali titik dua.
name = "Elixir"
year = 2012
IO.puts("#{name} since #{year}") # Elixir since 2012
IO.puts(10 / 4) # 2.5 (always a float)
IO.puts(div(10, 4)) # 2
IO.puts(rem(10, 4)) # 2
IO.puts("ab" <> "cd") # abcd (string concatenation)
IO.puts(String.length("héllo")) # 5 (counts characters, not bytes)
IO.inspect(:ok) # :ok
IO.inspect(is_atom(true)) # true (booleans are atoms)| Tipe | Contoh | Catatan |
|---|---|---|
| integer | 42, 1_000_000 | presisi arbitrer |
| float | 3.14 | presisi ganda 64-bit |
| atom | :ok, :error | konstanta; true, false, dan nil adalah atom |
| string | "hello" | binary UTF-8; interpolasi dengan #{} |
| list | [1, 2, 3] | linked list |
| tuple | {:ok, 1} | ukuran tetap, disimpan berurutan di memori |
| map | %{"a" => 1} | penyimpanan key-value |
Pattern matching
Di Elixir, = adalah operator match. Sisi kiri adalah pattern; jika sisi kanan cocok dengannya, variabel dalam pattern akan terikat. Pattern matching dipakai di mana-mana: pada assignment, case, kepala fungsi, dan penanganan error.
{:ok, value} = {:ok, 42}
IO.puts(value) # 42
[first | rest] = [1, 2, 3]
IO.inspect({first, rest}) # {1, [2, 3]}
%{name: n} = %{name: "Ann", age: 31}
IO.puts(n) # Ann
result = {:error, :not_found}
message =
case result do
{:ok, v} -> "got #{v}"
{:error, reason} -> "failed: #{reason}"
end
IO.puts(message) # failed: not_found
x = 1
^x = 1 # pin operator: match the current valueJika pattern tidak cocok, Elixir memunculkan MatchError. Operator pin ^ berarti "pakai nilai variabel yang sudah ada" alih-alih mengikatnya ulang.
Alur kontrol
Elixir punya case, cond, dan if/unless, dan semuanya mengembalikan nilai. Tidak ada perulangan for/while yang mengubah penghitung; sebagai gantinya Anda memakai rekursi, modul Enum, atau comprehension for.
temp = 22
feel =
cond do
temp > 30 -> "hot"
temp >= 15 -> "mild"
true -> "cold"
end
IO.puts(feel) # mild
IO.puts(if(rem(temp, 2) == 0, do: "even", else: "odd")) # even
squares = for n <- 1..5, rem(n, 2) == 1, do: n * n
IO.inspect(squares) # [1, 9, 25]Hanya false dan nil yang falsy; semua yang lain, termasuk 0 dan "", adalah truthy.
Fungsi, modul, dan operator pipe
Fungsi bernama berada di dalam modul yang didefinisikan dengan defmodule. Sebuah fungsi dapat punya beberapa clause, yang dipilih lewat pattern matching dan guard (when). Fungsi anonim ditulis fn ... end dan dipanggil dengan tanda titik. Operator pipe |> meneruskan hasil satu ekspresi sebagai argumen pertama pada pemanggilan berikutnya.
defmodule Math do
def factorial(0), do: 1
def factorial(n) when n > 0, do: n * factorial(n - 1)
def sign(n) when n < 0, do: :negative
def sign(0), do: :zero
def sign(_n), do: :positive
end
IO.puts(Math.factorial(5)) # 120
IO.inspect(Math.sign(-3)) # :negative
double = fn x -> x * 2 end
IO.puts(double.(21)) # 42 (note the dot)
" Elixir is fun "
|> String.trim()
|> String.upcase()
|> String.split()
|> IO.inspect() # ["ELIXIR", "IS", "FUN"]Fungsi dikenali dari nama dan arity (jumlah argumen), yang dalam dokumentasi ditulis seperti String.split/1 atau Math.factorial/1.
Koleksi: list, map, keyword list, dan Enum
Modul Enum bekerja dengan koleksi enumerable apa pun dan menyediakan map, filter, reduce, sort_by, group_by, dan banyak lagi. Map adalah struktur key-value utama; keyword list, seperti [size: 10, color: :red], umum dipakai untuk opsi.
nums = [5, 3, 8, 1]
IO.inspect(Enum.sort(nums)) # [1, 3, 5, 8]
IO.inspect(Enum.map(nums, &(&1 * 10))) # [50, 30, 80, 10]
IO.inspect(Enum.filter(nums, fn n -> n > 2 end)) # [5, 3, 8]
IO.puts(Enum.reduce(nums, 0, &+/2)) # 17
user = %{name: "Ann", age: 31}
older = %{user | age: 32} # update an existing key
IO.puts(user.age) # 31 (unchanged)
IO.puts(older.age) # 32
with_city = Map.put(user, :city, "Oslo")
IO.puts(with_city.city) # Oslo
IO.inspect(Map.get(user, :email, "none")) # "none"Operator capture & membuat fungsi anonim singkat: &(&1 * 10) berarti fn x -> x * 10 end, dan &+/2 merujuk ke fungsi + dengan dua argumen.
Proses dan konkurensi
Fitur khas Elixir adalah proses BEAM: unit eksekusi yang sangat ringan dan terisolasi dengan memorinya sendiri. Proses hanya berkomunikasi dengan mengirim pesan, dan satu mesin dapat menjalankan ratusan ribu proses. Supervisor me-restart proses yang crash, dan inilah dasar pendekatan "let it crash" Elixir untuk ketahanan terhadap gangguan.
parent = self()
spawn(fn ->
send(parent, {:result, 6 * 7})
end)
receive do
{:result, value} -> IO.puts("Got #{value}") # Got 42
after
1_000 -> IO.puts("timeout")
end
tasks = for n <- 1..3, do: Task.async(fn -> n * n end)
IO.inspect(Task.await_many(tasks)) # [1, 4, 9]Di aplikasi nyata Anda jarang memanggil spawn secara langsung. Anda memakai abstraksi dari OTP seperti Task, GenServer, Agent, dan Supervisor.
Penanganan error
Elixir yang idiomatis mengembalikan tagged tuple seperti {:ok, value} atau {:error, reason} untuk kegagalan yang sudah diperkirakan dan memakai case atau with untuk menanganinya. Fungsi yang namanya berakhiran !, seperti File.read!/1, memunculkan exception sebagai gantinya. Exception dapat ditangkap dengan try/rescue, tetapi ini jarang dilakukan.
case File.read("missing.txt") do
{:ok, text} -> IO.puts(text)
{:error, reason} -> IO.puts("Cannot read: #{reason}") # Cannot read: enoent
end
with {a, ""} <- Integer.parse("40"),
{b, ""} <- Integer.parse("2") do
IO.puts(a + b) # 42
else
_ -> IO.puts("not a whole number")
end
try do
String.to_integer("abc")
rescue
e in ArgumentError -> IO.puts("Error: #{Exception.message(e)}")
endwith merangkai beberapa match dan berhenti pada yang pertama tidak cocok, lalu mengembalikan nilai tersebut. Ini menghindari ekspresi case yang bersarang dalam.
Program lengkap kecil: frekuensi kata
Script ini menghitung kata dalam sebuah teks dan mencetak tiga kata yang paling sering muncul. Ini adalah pipeline Elixir yang khas, disusun dari fungsi String dan Enum.
defmodule WordFreq do
def top(text, n) do
text
|> String.downcase()
|> String.split(~r/[^a-z']+/, trim: true)
|> Enum.frequencies()
|> Enum.sort_by(fn {word, count} -> {-count, word} end)
|> Enum.take(n)
end
end
"The cat and the dog. The dog sat, and the cat ran."
|> WordFreq.top(3)
|> Enum.each(fn {word, count} -> IO.puts("#{word}: #{count}") end)
# the: 4
# and: 2
# cat: 2Langkah selanjutnya
- Panduan resmi Getting Started di hexdocs.pm dan situs elixir-lang.org.
- HexDocs, tempat dokumentasi Elixir dan setiap paket Hex dipublikasikan.
- Panduan framework Phoenix untuk pengembangan web.
- Elixir School, kurikulum komunitas yang gratis, dan jalur Elixir di Exercism untuk berlatih.
FAQ
Apa perbedaan Elixir dan Erlang?
Keduanya berjalan di virtual machine BEAM yang sama dan dapat saling memanggil kode. Elixir menambahkan sintaks yang terinspirasi Ruby, macro, operator pipe, dan tool seperti Mix dan Hex, sedangkan Erlang adalah bahasa yang lebih tua tempat OTP dibangun.
Apakah Elixir bagus untuk pengembangan web?
Ya. Framework Phoenix cepat dan menangani banyak koneksi simultan, dan LiveView memungkinkan Anda membangun halaman interaktif dengan HTML yang dirender server dan sedikit JavaScript.
Apakah Elixir bertipe statis?
Elixir bertipe dinamis. Versi terbaru menambahkan sistem tipe bertahap berbasis teori himpunan ke dalam compiler, dan banyak tim juga memakai Dialyzer dengan typespec untuk menemukan error tipe.
Apa arti |> di Elixir?
|> adalah operator pipe. x |> f(y) sama dengan f(x, y), sehingga rangkaian transformasi dapat dibaca dari atas ke bawah.
Apa itu file .ex dan .exs?
File .ex dikompilasi sebagai bagian dari proyek, sedangkan file .exs adalah script yang diinterpretasikan saat dijalankan, umum dipakai untuk test dan konfigurasi.