agentsclimarketplace

Elixir

Skill sickn33/agentic-awesome-skills/plugins/agentic-awesome-skills-claude/skills/super-code/elixir

Language-specific super-code guidelines for elixir.From its SKILL.md

Install
npx -y skills add sickn33/agentic-awesome-skills --skill elixir

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

8.2 KB, ~2.4k tokens by cl100k_base, as published. Nobody here has run it

Elixir / Erlang: Idiomatic Efficiency Reference

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
  2. Pipe Operator & Transforms
  3. Processes & OTP
  4. Error Handling
  5. Collections & Enum
  6. Structs & Protocols
  7. Anti-patterns specific to Elixir/Erlang

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} -> 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.start(fn -> do_work() end)
# 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}
end

"Let it crash" — supervisors handle recovery. Don't defensively catch inside GenServers.


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-patternPreferred
spawn without link/monitorTask.start_link or GenServer
try/catch inside GenServerlet it crash; supervisor restarts
String error reasonsatom or struct errors
Enum.count(x) == 0Enum.empty?(x) or match?([], x)
Mutable-style accumulatorEnum.reduce / recursion
if/else chain on data shapemulti-clause function + pattern match
Nested case for ok/errorwith expression
IO.inspect left in prodLogger with levels
Single-step pipedirect function call
Enum on huge/infinite dataStream
Raw PID passingnamed 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 wrapperGenServer 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.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,144. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.