elixir – skill Lập trình cho agent AI
Hướng dẫn mã nguồn chuyên biệt cho Elixir.
Cài skill elixir bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/elixir-ee6e1.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /elixir – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/elixir-ee6e1.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $elixir – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/elixir-ee6e1.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /elixir – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/elixir-ee6e1.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /elixir – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
Elixir / Erlang: Idiomatic Efficiency Reference
Apply only within the requested scope and current project conventions. Preserve behavior, ownership, error handling, accessibility and security; examples are independent fragments, not equivalent rewrites in every context. Verify language/library versions and run relevant checks. Required tests and user-facing progress/results take precedence over brevity rules. Do not install dependencies or delete files merely to follow these examples.
When to Use
- Use this skill when the task matches this description: Language-specific super-code guidelines for elixir.
Table of Contents
1. [Pattern Matching & Guards](#patterns)
2. [Pipe Operator & Transforms](#pipes)
3. [Processes & OTP](#otp)
4. [Error Handling](#errors)
5. [Collections & Enum](#collections)
6. [Structs & Protocols](#structs)
7. [Anti-patterns specific to Elixir/Erlang](#antipatterns)
---
1. Pattern Matching & Guards {#patterns}
# ❌ Extracting with Map.get then checking
value = Map.get(map, :key)
if value != nil do
process(value)
end
# ✅ — pattern match directly
case map do
%{key: value} when not is_nil(value) -> process(value)
_ -> :noop
end
# or with if:
if value = map[:key], do: process(value)# ❌ Nested case for multiple conditions
case fetch_user(id) do
{:ok, user} ->
case validate(user) do
{:ok, valid_user} -> save(valid_user)
{:error, reason} -> {:error, reason}
end
{:error, reason} -> {:error, reason}
end
# ✅ — with clause
with {:ok, user} <- fetch_user(id),
{:ok, valid_user} <- validate(user) do
save(valid_user)
end# ❌ if/else for known shapes
def area(shape) do
if shape.type == :circle do
:math.pi() * shape.radius * shape.radius
else
shape.width * shape.height
end
end
# ✅ — multi-clause function with pattern match
def area(%{type: :circle, radius: r}), do: :math.pi() * r * r
def area(%{type: :rect, width: w, height: h}), do: w * h# ❌ Checking type at runtime
def process(x) do
if is_integer(x) and x > 0 do
x * 2
end
end
# ✅ — guard clause
def process(x) when is_integer(x) and x > 0, do: x * 2
def process(_), do: {:error, :invalid_input}---
2. Pipe Operator & Transforms {#pipes}
# ❌ Nested function calls
String.trim(String.downcase(String.replace(input, ~r/\s+/, " ")))
# ✅
input
|> String.replace(~r/\s+/, " ")
|> String.downcase()
|> String.trim()# ❌ Pipe into anonymous function awkwardly
data
|> (fn x -> x * 2 end).()
# ✅ — use then/1 or named function
data
|> then(&(&1 * 2))
# or better: extract a named function
data |> double()# ❌ Single-step pipe (no gain in readability)
result = list |> Enum.count()
# ✅ — direct call for single operation
result = Enum.count(list)Pipe when 2+ transforms. Direct call for single operation. First arg flows through pipe.
---
3. Processes & OTP {#otp}
# ❌ Raw spawn for stateful process
pid = spawn(fn -> loop(%{count: 0}) end)
send(pid, {:increment})
# ✅ — GenServer for stateful processes
defmodule Counter do
use GenServer
def start_link(init \\ 0), do: GenServer.start_link(__MODULE__, init)
def increment(pid), do: GenServer.call(pid, :increment)
@impl true
def init(count), do: {:ok, count}
@impl true
def handle_call(:increment, _from, count), do: {:reply, count + 1, count + 1}
end# ❌ Spawning without linking (orphan process on crash)
spawn(fn -> do_work() end)
# ✅ — Task for fire-and-forget with supervision
Task.Supervisor.start_child(MyTaskSupervisor, fn -> do_work() end) # supervisor must already be started
# or for awaitable result:
task = Task.async(fn -> do_work() end)
result = Task.await(task)# ❌ Manual process registry
Process.register(self(), :my_worker)
# ✅ — use Registry or named GenServer
{:ok, _} = Registry.start_link(keys: :unique, name: MyRegistry)
GenServer.start_link(Worker, arg, name: {:via, Registry, {MyRegistry, :my_worker}})# ❌ try/catch in GenServer (breaks supervision)
def handle_call(:work, _from, state) do
try do
result = risky_operation()
{:reply, result, state}
catch
_ -> {:reply, :error, state}
end
end
# ✅ — let it crash; supervisor restarts
def handle_call(:work, _from, state) do
result = risky_operation()
{:reply, result, state}
endUse supervisors for unexpected failures; handle expected domain failures explicitly. Restarts do not roll back external effects, so require idempotency and recovery planning.
---
4. Error Handling {#errors}
# ❌ Raising for expected failures
def find_user(id) do
case Repo.get(User, id) do
nil -> raise "User not found"
user -> user
end
end
# ✅ — tagged tuples for expected outcomes
def find_user(id) do
case Repo.get(User, id) do
nil -> {:error, :not_found}
user -> {:ok, user}
end
end# ❌ Ignoring error tuple
{:ok, result} = might_fail() # crashes on {:error, _}
# ✅ — handle both cases
case might_fail() do
{:ok, result} -> process(result)
{:error, reason} -> Logger.error("Failed: #{inspect(reason)}")
end# ❌ String errors
{:error, "something went wrong"}
# ✅ — atom or struct errors (matchable, cheap)
{:error, :timeout}
{:error, %ValidationError{field: :email, reason: :invalid_format}}# ❌ Deep nesting of ok/error checks
case step1() do
{:ok, a} ->
case step2(a) do
{:ok, b} ->
case step3(b) do
{:ok, c} -> {:ok, c}
error -> error
end
error -> error
end
error -> error
end
# ✅
with {:ok, a} <- step1(),
{:ok, b} <- step2(a),
{:ok, c} <- step3(b) do
{:ok, c}
else
{:error, reason} -> {:error, reason}
end---
5. Collections & Enum {#collections}
# ❌ Multiple passes when one suffices
items
|> Enum.filter(&(&1.active))
|> Enum.map(&(&1.name))
# ✅ — for comprehension when filter + transform
for %{active: true, name: name} <- items, do: name# ❌ Enum.count for empty check (traverses whole list)
if Enum.count(list) == 0, do: :empty
# ✅
if Enum.empty?(list), do: :empty
# or pattern match:
case list do
[] -> :empty
_ -> :has_items
end# ❌ Building map with Enum.reduce when Map.new works
Enum.reduce(users, %{}, fn user, acc -> Map.put(acc, user.id, user) end)
# ✅
Map.new(users, &{&1.id, &1})# ❌ Enum on large dataset (eager — builds intermediate lists)
huge_list
|> Enum.map(&transform/1)
|> Enum.filter(&valid?/1)
|> Enum.take(10)
# ✅ — Stream for lazy evaluation
huge_list
|> Stream.map(&transform/1)
|> Stream.filter(&valid?/1)
|> Enum.take(10)Use Stream when chaining transforms on large/infinite collections. Enum for small or final step.
---
6. Structs & Protocols {#structs}
# ❌ Plain map for domain entities
user = %{name: "Alice", email: "[email protected]", age: 30}
# typo in key goes unnoticed: user.emaail
# ✅ — struct enforces keys
defmodule User do
@enforce_keys [:name, :email]
defstruct [:name, :email, age: 0]
end
user = %User{name: "Alice", email: "[email protected]"}# ❌ Protocol with only one implementation (over-abstraction)
defprotocol Renderable do
def render(data)
end
defimpl Renderable, for: HtmlPage do ... end
# ✅ — just a function until you need polymorphism
def render(%HtmlPage{} = page), do: ...# ❌ Updating nested struct manually
updated = %{user | address: %{user.address | city: "NYC"}}
# ✅
updated = put_in(user.address.city, "NYC")
# or Kernel.update_in/3 for transforms---
7. Anti-patterns specific to Elixir/Erlang {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| spawn without link/monitor | Task.start_link or GenServer |
| try/catch inside GenServer | let it crash; supervisor restarts |
| String error reasons | atom or struct errors |
| Enum.count(x) == 0 | Enum.empty?(x) or match?([], x) |
| Mutable-style accumulator | Enum.reduce / recursion |
| if/else chain on data shape | multi-clause function + pattern match |
| Nested case for ok/error | with expression |
| IO.inspect left in prod | Logger with levels |
| Single-step pipe | direct function call |
| Enum on huge/infinite data | Stream |
| Raw PID passing | named processes / Registry |
| Boolean returns for success/fail | {:ok, val} / {:error, reason} tuples |
| length(list) > 0 (O(n)) | pattern match [_ | _] |
| Shared mutable state via ETS without wrapper | GenServer or Agent as access layer |
Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
Skill liên quan
- track-management – Dùng skill này khi tạo, quản lý hoặc làm việc với track Conductor — đơn vị công việc…
- pw-review – Đánh giá chất lượng test Playwright. Dùng khi người dùng nói “review test”, “kiểm tra…
- system-atlas – Xây dựng atlas kiến trúc isometric có thể khám phá dưới dạng HTML.
- turborepo-caching – Cấu hình Turborepo để build monorepo hiệu quả với cache cục bộ và từ xa. Dùng khi thiết…
- systematic-debugging – Gỡ lỗi nguyên nhân gốc qua 4 giai đoạn: hiểu lỗi trước khi sửa.
- employment-contract-templates – Mẫu và cách soạn bản nháp tài liệu lao động để luật sư địa phương rà soát, gồm hợp đồng…
- typescript-advanced-types – Làm chủ hệ thống kiểu nâng cao của TypeScript gồm generics, conditional types, mapped…
- red-green-proof – Biến lỗi nghi ngờ thành lỗi đã được chứng minh. Kiểm chứng nguyên nhân với thực tế trước…