Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/deps
erl_crash.dump
*.ez
.tool-versions
113 changes: 113 additions & 0 deletions CLAUDE_THOUGHTS_ON_KOANS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Claude's Analysis of Elixir Koans

## Overall Assessment

The Elixir koans provide a solid foundation for learning Elixir's core concepts through hands-on practice. The progression from basic data types to advanced concurrency concepts follows a logical learning path that builds knowledge incrementally.

## Strengths

### 1. **Excellent Progression and Coverage**
- Well-structured from fundamentals (equalities, strings, numbers) to advanced topics (processes, GenServers, protocols)
- Covers all essential Elixir data types and concepts systematically
- Good balance between breadth and depth

### 2. **Interactive Learning Approach**
- The fill-in-the-blank (`___`) format encourages active engagement
- Immediate feedback through test execution
- Zen-like koan naming creates an engaging learning atmosphere

### 3. **Strong Foundation Building**
- **Basic Types**: Numbers, strings, atoms, booleans are well covered
- **Data Structures**: Comprehensive coverage of lists, tuples, maps, keyword lists, MapSets, and structs
- **Advanced Features**: Pattern matching, functions, enums, and comprehensions are thoughtfully presented

### 4. **Concurrency Excellence**
- Outstanding coverage of Elixir's actor model with processes, Tasks, Agents, and GenServers
- Practical examples showing message passing, state management, and supervision
- Good introduction to OTP concepts

## Areas for Improvement

### 1. **Missing Fundamental Concepts**
- **Pipe Operator**: Only briefly mentioned in functions.ex:104-111, but deserves dedicated coverage as it's idiomatic Elixir
- **with Statement**: Missing entirely - important for error handling and nested operations
- **Case/Cond/If Statements**: Only case is briefly shown in pattern matching
- **Guard Clauses**: Mentioned in functions but could use more comprehensive coverage
- **Binary Pattern Matching**: Missing - important for working with binary data

### 2. **Limited Error Handling**
- Only basic error tuple patterns (`{:ok, value}`, `{:error, reason}`) are shown
- Missing `try/catch/rescue/after` constructs
- No coverage of custom exception types
- Could benefit from more comprehensive error handling patterns

### 3. **Module System Gaps**
- Basic module definition shown but missing:
- Module attributes beyond `@moduledoc`
- Import/alias/require directives
- Module compilation hooks
- Behaviors beyond GenServer

### 4. **Syntax and Language Features**
- **Documentation**: No coverage of `@doc` or doctests
- **Typespecs**: Missing `@spec` and `@type` - important for larger codebases
- **Macros**: Not covered (though perhaps too advanced for koans)
- **Use/Import/Alias**: Mentioned but not explained

### 5. **Practical Application**
- Most examples are abstract - could benefit from more real-world scenarios
- Missing file I/O operations
- No coverage of common patterns like supervision trees
- HTTP client/server basics could be valuable

## Outdated or Problematic Areas

### 1. **Syntax Updates Needed**
- All syntax appears current for modern Elixir (1.14+)
- No deprecated functions or patterns identified

### 2. **Best Practices Alignment**
- Code follows current Elixir style guidelines
- Function definitions and module structures are idiomatic

### 3. **Minor Issues**
- Line 113 in Numbers.ex uses pattern matching syntax that's slightly advanced for its position
- Some variable names could be more descriptive in complex examples

## Recommended Additions

### 1. **New Koans to Add**
```
21_control_flow.ex # if/unless/cond/case comprehensive coverage
22_error_handling.ex # try/catch/rescue/after, error tuples
23_pipe_operator.ex # |>, then/2, comprehensive piping patterns
24_with_statement.ex # with clauses, error handling patterns
25_binary_matching.ex # <<>>, binary patterns, string manipulation
26_module_attributes.ex # @doc, @spec, @type, compile-time attributes
27_io_and_files.ex # File operations, IO operations
28_otp_behaviors.ex # Custom behaviors, supervision basics
```

### 2. **Enhanced Existing Koans**
- **Functions**: Add more pipe operator examples and capture syntax variations
- **Pattern Matching**: Include more binary pattern matching examples
- **GenServers**: Add supervision and error handling examples
- **Enums**: Include Stream module basics for lazy evaluation

### 3. **Pedagogical Improvements**
- Add more real-world context to abstract examples
- Include common pitfalls and "gotcha" moments
- Add exercises that build on previous koans
- Include performance considerations where relevant

## Conclusion

The Elixir koans are well-crafted and provide excellent coverage of Elixir's core concepts. They successfully teach the fundamentals and introduce advanced topics in a logical progression. The main gaps are in practical error handling, advanced control flow, and some modern Elixir idioms.

**Recommendation**: These koans do a good job introducing Elixir basics. The most impactful improvements would be:
1. Adding dedicated coverage for the pipe operator and `with` statement
2. Expanding error handling beyond basic tuple patterns
3. Including more real-world, practical examples
4. Adding binary pattern matching for string/data processing

The current koans provide a solid foundation, but learners would benefit from supplementary material covering the missing concepts before moving to production Elixir development.
44 changes: 41 additions & 3 deletions lib/koans/12_pattern_matching.ex
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ defmodule PatternMatching do
end

koan "Errors are shaped differently than successful results" do
dog = %{type: "dog"}
dog = %{type: "barking"}

result =
type =
case Map.fetch(dog, :type) do
{:ok, value} -> value
:error -> "not present"
end

assert result == ___
assert type == ___
end

defmodule Animal do
Expand Down Expand Up @@ -166,4 +166,42 @@ defmodule PatternMatching do
^a = ___
end
end

koan "Pattern matching works with nested data structures" do
user = %{
profile: %{
personal: %{name: "Alice", age: 30},
settings: %{theme: "dark", notifications: true}
}
}

%{profile: %{personal: %{age: age}, settings: %{theme: theme}}} = user
assert age == ___
assert theme == ___
end

koan "Lists can be pattern matched with head and tail" do
numbers = [1, 2, 3, 4, 5]

[first, second | rest] = numbers
assert first == ___
assert second == ___
assert rest == ___

[head | _tail] = numbers
assert head == ___
end

koan "Pattern matching can extract values from function return tuples" do
divide = fn
_, 0 -> {:error, :division_by_zero}
x, y -> {:ok, x / y}
end

{:ok, result} = divide.(10, 2)
assert result == ___

{:error, reason} = divide.(10, 0)
assert reason == ___
end
end
34 changes: 34 additions & 0 deletions lib/koans/13_functions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,28 @@ defmodule Functions do
assert result == ___
end

koan "Pipes make data transformation pipelines readable" do
numbers = [1, 2, 3, 4, 5]

result =
numbers
|> Enum.filter(&(&1 > 2))
|> Enum.map(&(&1 * 2))
|> Enum.sum()

assert result == ___

user_input = " Hello World "

cleaned =
user_input
|> String.trim()
|> String.downcase()
|> String.replace(" ", "_")

assert cleaned == ___
end

koan "Conveniently keyword lists can be used for function options" do
transform = fn str, opts ->
if opts[:upcase] do
Expand All @@ -122,4 +144,16 @@ defmodule Functions do
assert transform.("good", upcase: true) == ___
assert transform.("good", upcase: false) == ___
end

koan "Anonymous functions can use the & capture syntax for very concise definitions" do
add_one = &(&1 + 1)
multiply_by_two = &(&1 * 2)

result = 5 |> add_one.() |> multiply_by_two.()
assert result == ___

# You can also capture existing functions
string_length = &String.length/1
assert string_length.("hello") == ___
end
end
52 changes: 48 additions & 4 deletions lib/koans/14_enums.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,18 @@ defmodule Enums do
assert Enum.count([1, 2, 3]) == ___
end

koan "Depending on the type, it counts pairs" do
assert Enum.count(%{a: :foo, b: :bar}) == ___
koan "Counting is similar to length" do
assert length([1, 2, 3]) == ___
end

koan "But it allows you to count certain elements" do
assert Enum.count([1, 2, 3], &(&1 == 2)) == ___
end

koan "Depending on the type, it counts pairs while length does not" do
map = %{a: :foo, b: :bar}
assert Enum.count(map) == ___
assert_raise ___, fn -> length(map) end
end

def less_than_five?(n), do: n < 5
Expand All @@ -34,7 +44,7 @@ defmodule Enums do

def multiply_by_ten(n), do: 10 * n

koan "Map converts each element of a list by running some function with it" do
koan "Mapping converts each element of a list by running some function with it" do
assert Enum.map([1, 2, 3], &multiply_by_ten/1) == ___
end

Expand Down Expand Up @@ -66,7 +76,7 @@ defmodule Enums do
assert Enum.zip(letters, numbers) == ___
end

koan "When you want to find that one pesky element" do
koan "When you want to find that one pesky element, it returns the first" do
assert Enum.find([1, 2, 3, 4], &even?/1) == ___
end

Expand All @@ -83,4 +93,38 @@ defmodule Enums do
koan "Collapse an entire list of elements down to a single one by repeating a function." do
assert Enum.reduce([1, 2, 3], 0, fn element, accumulator -> element + accumulator end) == ___
end

koan "Enum.chunk_every splits lists into smaller lists of fixed size" do
assert Enum.chunk_every([1, 2, 3, 4, 5, 6], 2) == ___
assert Enum.chunk_every([1, 2, 3, 4, 5], 3) == ___
end

koan "Enum.flat_map transforms and flattens in one step" do
result =
[1, 2, 3]
|> Enum.flat_map(&[&1, &1 * 10])

assert result == ___
end

koan "Enum.group_by organizes elements by a grouping function" do
words = ["apple", "banana", "cherry", "apricot", "blueberry"]
grouped = Enum.group_by(words, &String.first/1)

assert grouped["a"] == ___
assert grouped["b"] == ___
end

koan "Stream provides lazy enumeration for large datasets" do
# Streams are lazy - they don't execute until you call Enum on them
stream =
1..1_000_000
|> Stream.filter(&even?/1)
|> Stream.map(&(&1 * 2))
|> Stream.take(3)

# Nothing has been computed yet!
result = Enum.to_list(stream)
assert result == ___
end
end
82 changes: 82 additions & 0 deletions lib/koans/18_genservers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,86 @@ defmodule GenServers do

:ok = Laptop.stop()
end

defmodule TimeoutServer do
use GenServer

def start_link(timeout) do
GenServer.start_link(__MODULE__, timeout, name: __MODULE__)
end

def init(timeout) do
{:ok, %{count: 0}, timeout}
end

def get_count do
GenServer.call(__MODULE__, :get_count)
end

def handle_call(:get_count, _from, state) do
{:reply, state.count, state}
end

def handle_info(:timeout, state) do
new_state = %{state | count: state.count + 1}
{:noreply, new_state}
end
end

koan "GenServers can handle info messages and timeouts" do
{:ok, _pid} = TimeoutServer.start_link(100)
# Wait for timeout to occur
:timer.sleep(101)
count = TimeoutServer.get_count()
assert count == ___

GenServer.stop(TimeoutServer)
end

defmodule CrashableServer do
use GenServer

def start_link(initial) do
GenServer.start_link(__MODULE__, initial, name: __MODULE__)
end

def init(initial) do
{:ok, initial}
end

def crash do
GenServer.cast(__MODULE__, :crash)
end

def get_state do
GenServer.call(__MODULE__, :get_state)
end

def handle_call(:get_state, _from, state) do
{:reply, state, state}
end

def handle_cast(:crash, _state) do
raise "Intentional crash for testing"
end
end

koan "GenServers can be supervised and restarted" do
# Start under a supervisor
children = [{CrashableServer, "the state"}]
{:ok, supervisor} = Supervisor.start_link(children, strategy: :one_for_one)

# Server should be running
initial_state = CrashableServer.get_state()
assert initial_state == ___

:ok = CrashableServer.crash()
# Wait for recovery
:timer.sleep(100)

state_after_crash_recovery = CrashableServer.get_state()
assert state_after_crash_recovery == ___

Supervisor.stop(supervisor)
end
end
Loading