Elixir basics · Lesson 28 (open contents)
01 · Let the code speak02 · Meet six kinds of values03 · Put values together04 · Build a new list05 · Make the shapes match06 · Choose with case07 · Parse a number safely08 · Unpack &1 and the pipe09 · Put code in a project10 · Ask precise true-or-false questions11 · Read and update nested data12 · Count visible text and bytes13 · Let function clauses choose14 · Solve a list twice15 · Choose the smallest clear control form16 · Give domain data a shape17 · Put one tested module in Mix18 · Make success and failure predictable19 · Read and write one real file20 · Take only the data you need21 · Give different data one shared action22 · Write a callback contract23 · Write down the data contract24 · Test behavior and boundaries25 · Turn a project into a command26 · Write the project promise27 · Parse one real line28 · Keep the public API small29 · Make the promise executable30 · Package the edge and leave clues31 · Prove the project is done
ELIXIR · PROJECT · LESSON 2835 minutes

Keep the public API small

Separate file access, parsing, and pure summary logic.

01 · Start with the whole example

Run this first

Run it once, change one input, and compare the new result.

Elixir
# Expose a small parser and one summary call
defmodule Cleaner do
  @levels %{"INFO" => :info, "WARN" => :warn, "ERROR" => :error}

  def parse_line(line) do
    case String.split(String.trim(line), ~r/\s+/, parts: 2) do
      [""] -> :skip
      [level, message] ->
        case Map.fetch(@levels, level) do
          {:ok, tag} -> {:ok, {tag, message}}
          :error -> {:error, :invalid_line}
        end
      _ -> {:error, :invalid_line}
    end
  end

  def run(records), do: {:ok, count(records)}
  defp count(records), do: Enum.frequencies_by(records, fn {level, _message} -> level end)
end

Cleaner.run([{:info, "ready"}, {:warn, "hot"}, {:info, "done"}])
Check the result first
  • The API returns {:ok, %{info: 2, warn: 1}}.
02 · Take the code apart

Read from the first line down

  1. Read def run/1: Expose one project entry point.
  2. Read defp: Keep an implementation helper private.
  3. Read Enum.reduce/3: Build the summary in a pure fold.
03 · Meet the new symbols

Symbols are not secret signs

def run/1

Expose one project entry point.

defp

Keep an implementation helper private.

Enum.reduce/3

Build the summary in a pure fold.

04 · Ideas inside the code

Match each name to its meaning

01

public API

A small public API gives callers fewer contracts to learn and protects internal changes.

02

pure core

Pure functions transform supplied data without opening files or changing outside state.

03

boundary module

A boundary module performs IO and converts its errors into project results.

05 · Make the idea clear

Why these forms are useful

Run the example first. Predict one result, then change one input and run it again.

A small public API gives callers fewer contracts to learn and protects internal changes.

Pure functions transform supplied data without opening files or changing outside state.

06 · Change it yourself

Close the answer and try

Add a public total/1 that returns the number of records.

Practice starting point
def total(records), do: ____

Target result: Return the list length.

Stuck? Read one hint

Use length/1.

After you run it, see one answer
One answer
def total(records), do: length(records)
Think about it: Should callers invoke `count/1`?

No. defp keeps it private so the module can change its implementation.

Take with you

Remember these three lines

  1. 1

    Expose a narrow API.

  2. 2

    Keep transformations pure.

  3. 3

    Put IO and error conversion at boundaries.

Lesson completeRun the code once, then mark this lesson as done.