Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Language-specific super-code guidelines for elixir.
.claude/skills/lingxling-elixir/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 113% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 110% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 56% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 118% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 114% | 0% |
elixir# ❌ 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)
elixir# ❌ 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
elixir# ❌ 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
elixir# ❌ 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}
elixir# ❌ Nested function calls String.trim(String.downcase(String.replace(input, ~r/\s+/, " "))) # ✅ input |> String.replace(~r/\s+/, " ") |> String.downcase() |> String.trim()
elixir# ❌ 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()
elixir# ❌ 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.
elixir# ❌ 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
elixir# ❌ 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)
elixir# ❌ 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}})
elixir# ❌ 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.
elixir# ❌ 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
elixir# ❌ 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
elixir# ❌ String errors {:error, "something went wrong"} # ✅ — atom or struct errors (matchable, cheap) {:error, :timeout} {:error, %ValidationError{field: :email, reason: :invalid_format}}
elixir# ❌ 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
elixir# ❌ 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
elixir# ❌ 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
elixir# ❌ 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})
elixir# ❌ 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.
elixir# ❌ Plain map for domain entities user = %{name: "Alice", email: "a@b.com", 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: "a@b.com"}
elixir# ❌ 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: ...
elixir# ❌ 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
| 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 |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 11,495 | 19,151 | +67% | 1 | 1 | 0% | 2,306 | 4,902 | +113% | 0 | 0 | — |
case-02 | pass→pass | 9,914 | 6,446 | -35% | 1 | 1 | 0% | 1,812 | 3,799 | +110% | 0 | 0 | — |
case-03 | pass→pass | 15,959 | 6,392 | -60% | 1 | 1 | 0% | 2,354 | 3,663 | +56% | 0 | 0 | — |
case-04 | pass→pass | 11,163 | 8,335 | -25% | 1 | 1 | 0% | 1,710 | 3,734 | +118% | 0 | 0 | — |
case-05 | pass→pass | 11,372 | 4,548 | -60% | 1 | 1 | 0% | 1,606 | 3,435 | +114% | 0 | 0 | — |
case-06 | pass→pass | 5,860 | 3,127 | -47% | 1 | 1 | 0% | 804 | 3,070 | +282% | 0 | 0 | — |
case-07 | pass→pass | 9,883 | 6,765 | -32% | 1 | 1 | 0% | 1,402 | 3,612 | +158% | 0 | 0 | — |
case-08 | pass→pass | 12,921 | 13,480 | +4% | 1 | 1 | 0% | 2,348 | 4,522 | +93% | 0 | 0 | — |
case-09 | pass→pass | 11,938 | 7,164 | -40% | 1 | 1 | 0% | 1,585 | 3,723 | +135% | 0 | 0 | — |
case-10 | pass→pass | 15,031 | 15,339 | +2% | 1 | 1 | 0% | 2,363 | 4,892 | +107% | 0 | 0 | — |
case-11 | pass→pass | 14,864 | 10,701 | -28% | 1 | 1 | 0% | 2,135 | 4,124 | +93% | 0 | 0 | — |
case-12 | pass→pass | 14,125 | 7,733 | -45% | 1 | 1 | 0% | 1,991 | 3,937 | +98% | 0 | 0 | — |
case-13 | pass→pass | 13,742 | 10,218 | -26% | 1 | 1 | 0% | 1,966 | 4,090 | +108% | 0 | 0 | — |
case-14 | pass→pass | 5,079 | 2,473 | -51% | 1 | 1 | 0% | 876 | 3,087 | +252% | 0 | 0 | — |
case-15 | pass→pass | 18,400 | 10,231 | -44% | 1 | 1 | 0% | 1,769 | 4,006 | +126% | 0 | 0 | — |
case-16 | pass→pass | 13,429 | 5,918 | -56% | 1 | 1 | 0% | 2,505 | 3,725 | +49% | 0 | 0 | — |
case-17 | pass→pass | 7,661 | 6,322 | -17% | 1 | 1 | 0% | 1,328 | 3,406 | +156% | 0 | 0 | — |
case-18 | pass→pass | 10,105 | 5,400 | -47% | 1 | 1 | 0% | 1,535 | 3,562 | +132% | 0 | 0 | — |
case-19 | pass→pass | 9,211 | 5,356 | -42% | 1 | 1 | 0% | 1,490 | 3,340 | +124% | 0 | 0 | — |
case-20 | pass→pass | 12,838 | 10,783 | -16% | 1 | 1 | 0% | 2,338 | 4,187 | +79% | 0 | 0 | — |
case-21 | pass→pass | 11,994 | 5,612 | -53% | 1 | 1 | 0% | 1,400 | 3,420 | +144% | 0 | 0 | — |
case-22 | pass→pass | 20,378 | 10,271 | -50% | 1 | 1 | 0% | 2,899 | 4,061 | +40% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.