LEARN RAKU DEEP DIVE
Learn Raku: From Zero to Language Wizard
Goal: Deeply understand Raku (formerly Perl 6)—from its elegant syntax to its mind-bending features like grammars, junctions, and the meta-object protocol. Master a language that treats parsing, concurrency, and metaprogramming as first-class citizens.
Why Raku Matters
Raku is not just another programming language—it’s a language laboratory. Designed over 15 years by some of the brightest minds in programming language theory (including Larry Wall, Damian Conway, and many others), Raku incorporates ideas that other languages are only now beginning to explore:
- Built-in Grammars: Parse anything from simple data formats to entire programming languages
- Junctions: Quantum-inspired superpositions of values
- Gradual Typing: Mix dynamic and static typing freely
- First-class Concurrency: Promises, Supplies, and Channels built into the core
- Meta-Object Protocol: Modify the language itself at runtime
- Unicode-first Design: Native support for graphemes, not just bytes
After completing these projects, you will:
- Write parsers for any text format using grammars
- Build concurrent applications with reactive streams
- Understand and use multiple dispatch effectively
- Create domain-specific languages (DSLs)
- Modify classes and objects at runtime using the MOP
- Appreciate why “There’s More Than One Way To Do It” (TMTOWTDI)
Core Concept Analysis
The Raku Philosophy
"Raku is a language designed to grow with you."
— Larry Wall, Creator of Perl and Raku
Raku embraces contradictions:
- Simple for beginners, powerful for experts
- Concise when you want, explicit when you need
- Dynamic by default, typed when useful
- Object-oriented AND functional AND procedural
Fundamental Concepts
- Gradual Typing: Add types where they help, omit them where they don’t
sub greet($name) { ... } # No type sub greet(Str $name) { ... } # Typed parameter sub greet(Str $name --> Str) { ... } # Typed return - Sigils and Twigils: Variables announce their nature
$scalar- Single value@array- Positional (list)%hash- Associative (dictionary)&callable- Code reference- Twigils:
$*global,$.attribute,$!private
- Junctions: Values that are multiple things simultaneously
my $x = 1 | 2 | 3; # "any" junction if $input == $x { ... } # True if $input is 1, 2, OR 3 - Grammars: First-class parsing as a language feature
grammar JSON { rule TOP { <value> } rule value { <object> | <array> | <string> | <number> } ... } - Concurrency Primitives:
- Promise: A value that may not exist yet
- Supply: An asynchronous stream of values
- Channel: A thread-safe queue
- Multi-Dispatch: Functions dispatch based on argument types
multi sub process(Int $x) { ... } multi sub process(Str $x) { ... } multi sub process(42) { ... } # Literal dispatch! - Roles: Composition over inheritance
- Compile-time:
class Foo does Bar { } - Runtime:
$obj does SomeRole
- Compile-time:
- Meta-Object Protocol (MOP): The language is objects all the way down
- Every class has a metaclass
- You can introspect and modify anything
The Raku Ecosystem
┌─────────────────────────────────────────────────────────────┐
│ Your Raku Code │
├─────────────────────────────────────────────────────────────┤
│ Grammars │ Concurrency │ OOP/Roles │ Functional │
├─────────────────────────────────────────────────────────────┤
│ Raku Standard Library (core) │
├─────────────────────────────────────────────────────────────┤
│ Rakudo (Raku Implementation) │
├─────────────────────────────────────────────────────────────┤
│ MoarVM │ JVM │ JavaScript (backends) │
└─────────────────────────────────────────────────────────────┘
Project List
Projects are ordered from fundamentals to advanced Raku wizardry. Each explores a unique aspect of the language.
Project 1: Interactive Raku REPL Companion
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Raku-specific)
- Coolness Level: Level 2: Practical but Forgettable
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 1: Beginner
- Knowledge Area: Language Fundamentals / REPL
- Software or Tool: Rakudo
- Main Book: “Raku Fundamentals” by Moritz Lenz
What you’ll build: A personal “cheat sheet generator” that takes Raku concepts you’re learning and generates interactive examples you can run, modify, and explore. It will cover sigils, operators, control flow, and basic data structures.
Why it teaches Raku: Raku has unusual syntax (sigils, twigils, hyper operators, meta operators). The best way to internalize these is to experiment interactively. This project forces you to understand each feature deeply enough to explain it.
Core challenges you’ll face:
- Understanding sigils ($, @, %, &) → maps to how Raku thinks about data
- Learning the operator menagerie → maps to Raku’s expressive power
- Grasping context (item vs list) → maps to why things flatten or don’t
- Using the REPL effectively → maps to rapid experimentation
Key Concepts:
- Sigils and Variables: Raku Guide - Variables - Section 4
- Operators: “Raku Fundamentals” Chapter 3 - Moritz Lenz
- Control Flow: The Complete Course - Part 1
- Data Structures: “Using Raku” Chapters 1-3 - Andrew Shitov
Difficulty: Beginner Time estimate: Weekend Prerequisites: Basic programming in any language, Rakudo installed
Real world outcome:
$ raku cheat-companion.raku
╔════════════════════════════════════════════╗
║ Raku Interactive Cheat Companion ║
╚════════════════════════════════════════════╝
Choose a topic:
1. Sigils ($, @, %, &)
2. Operators (meta, hyper, reduction)
3. Control Flow (if, for, given/when)
4. Data Structures (Array, Hash, Set)
5. Quiz Me!
> 1
=== SIGILS ===
$ - Scalar (single value)
Example: my $name = "Raku";
Try it: [Interactive REPL opens]
@ - Positional (ordered collection)
Example: my @colors = <red green blue>;
Note: <> is quote words, like qw// in Perl
% - Associative (key-value pairs)
Example: my %ages = :alice(30), :bob(25);
Note: :key(val) is a Pair constructor
& - Callable (code reference)
Example: my &greet = sub ($name) { say "Hi, $name!" };
Call it: greet("World");
=== TWIGILS (secondary sigils) ===
$*var - Dynamic scope (global-ish)
$.attr - Public attribute accessor
$!attr - Private attribute
$?FILE - Compile-time constant
> 5
QUIZ: What does @array[*-1] return?
a) The first element
b) The last element
c) All elements except the last
d) An error
Your answer: b
✓ Correct! *-1 is a WhateverCode that means "end minus 1"
This is Raku's elegant way to index from the end.
Implementation Hints:
Structure your companion as a multi-dispatch system:
Questions to guide you:
- How do you read user input in Raku? (Hint:
prompt()or$*IN.get) - How do you organize topics? (Hint: use a Hash of Arrays or a class)
- How do you colorize terminal output? (Hint: Terminal::ANSIColor module)
- How do you dynamically evaluate Raku code? (Hint:
EVAL)
Start simple: hardcode a few examples per topic. Add interactivity incrementally.
Learning milestones:
- You can explain each sigil’s purpose → You understand Raku’s data model
- You understand context ($ vs @) → You grasp Raku’s coercion rules
- You’ve used 5+ unusual operators → You appreciate Raku’s expressiveness
- You can quiz yourself effectively → You’ve internalized the basics
Project 2: Data Format Parser with Grammars
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Grammar-specific)
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 2. The “Micro-SaaS / Pro Tool”
- Difficulty: Level 2: Intermediate
- Knowledge Area: Parsing / Grammars
- Software or Tool: Raku Grammars
- Main Book: “Raku Fundamentals” by Moritz Lenz
What you’ll build: A complete parser for a data format (INI, TOML, or a subset of JSON) using Raku’s built-in grammar system. Your parser will convert text into a Raku data structure with proper error reporting.
Why it teaches Raku: Grammars are Raku’s killer feature—no other mainstream language has parsing this deeply integrated. Understanding grammars unlocks Raku’s true power for text processing, DSLs, and language tools.
Core challenges you’ll face:
- Understanding tokens vs rules vs regex → maps to backtracking and whitespace handling
- Building grammar hierarchy → maps to composing parsers from pieces
- Writing grammar actions → maps to transforming parse trees into data
- Debugging grammars → maps to using Grammar::Tracer
Key Concepts:
- Grammar Basics: Grammar Tutorial - Official Docs
- Token vs Rule: Grammars Reference - Official Docs
- Actions: “Raku Fundamentals” Chapter 7 - Moritz Lenz
- Debugging: Andrew Shitov’s Grammar Series
Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Project 1, understanding of regular expressions
Real world outcome:
$ cat config.ini
[database]
host = localhost
port = 5432
name = myapp_production
[server]
address = 0.0.0.0
port = 8080
debug = true
$ raku ini-parser.raku config.ini
Parsing config.ini...
Parse tree:
INI::File
└─ Section: database
├─ host = localhost
├─ port = 5432
└─ name = myapp_production
└─ Section: server
├─ address = 0.0.0.0
├─ port = 8080
└─ debug = true
As Raku Hash:
{
database => {
host => "localhost",
port => "5432",
name => "myapp_production"
},
server => {
address => "0.0.0.0",
port => "8080",
debug => "true"
}
}
$ raku ini-parser.raku broken.ini
Parse FAILED at line 3, column 15:
Expected: '=' or end of line
Got: 'invalid token here'
Context: "host invalid token here"
^^^^^^^^^^^^^^
Implementation Hints:
Grammar structure for INI:
grammar INI {
token TOP { <section>* }
token section { <header> <keyvalue>* }
token header { '[' <identifier> ']' \n }
token keyvalue { <key> \s* '=' \s* <value> \n }
token key { <[\w-]>+ }
token value { \N* } # Everything except newline
token identifier { <[\w]>+ }
}
Actions class (transforms parse tree to data):
class INI::Actions {
method TOP($/) { make $<section>».made.hash }
method section($/) { make $<header>.made => $<keyvalue>».made.hash }
method header($/) { make ~$<identifier> }
method keyvalue($/) { make ~$<key> => ~$<value> }
}
Questions to guide you:
- What’s the difference between
token(no backtracking) andregex(backtracks)? - Why use
rulefor whitespace-significant parsing? - What does
$/represent? (The Match object) - How does
.madepropagate values up the parse tree?
Install Grammar::Tracer (zef install Grammar::Tracer) for debugging!
Learning milestones:
- Simple tokens match correctly → You understand basic patterns
- Nested structures parse → You understand grammar composition
- Actions produce Raku data → You understand parse tree transformation
- Error messages are helpful → You understand parse failure handling
Project 3: Junction-Powered Validation Library
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Junction-specific)
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 2. The “Micro-SaaS / Pro Tool”
- Difficulty: Level 2: Intermediate
- Knowledge Area: Junctions / Type System
- Software or Tool: Raku Core
- Main Book: “Using Raku” by Andrew Shitov
What you’ll build: A data validation library that uses junctions (any, all, one, none) to create expressive validation rules. Validate forms, API inputs, or configuration files with quantum-inspired logic.
Why it teaches Raku: Junctions are one of Raku’s most unique features—values that exist in superposition. Understanding them deeply reveals Raku’s elegant approach to boolean logic and autothreading.
Core challenges you’ll face:
- Understanding junction types (any, all, one, none) → maps to boolean algebra
- Grasping autothreading → maps to how operations distribute over junctions
- Junction collapse in boolean context → maps to when superposition resolves
- Combining junctions with smart matching → maps to powerful pattern matching
Key Concepts:
- Junction Basics: Junction Documentation - Official Docs
- Autothreading: RFC 225 on Superpositions
- Smart Matching: Raku Guide - Smart Match section
- Type Constraints: “Raku Fundamentals” Chapter 4 - Moritz Lenz
Difficulty: Intermediate Time estimate: 1 week Prerequisites: Project 1, understanding of boolean logic
Real world outcome:
$ raku validator-demo.raku
use Validator;
# Define validation rules using junctions
my $username-rule = Validator.new(
:name<username>,
:checks(
{ .chars >= 3 }, # At least 3 chars
{ .chars <= 20 }, # At most 20 chars
{ $_ ~~ /^<[a..z A..Z 0..9 _]>+$/ }, # Alphanumeric + underscore
{ $_ ne any(<admin root system>) }, # Not a reserved name
)
);
my $age-rule = Validator.new(
:name<age>,
:checks(
{ $_ ~~ Int }, # Must be integer
{ $_ >= 0 }, # Non-negative
{ $_ <= 150 }, # Reasonable maximum
)
);
my $email-rule = Validator.new(
:name<email>,
:checks(
{ .contains('@') },
{ .contains('.') },
{ .chars >= 5 },
)
);
# Validate some data
my %user = :username<alice_123>, :age(25), :email<alice@example.com>;
my $result = validate(%user, $username-rule, $age-rule, $email-rule);
say $result;
# Output:
# Validation Result: PASSED
# ✓ username: alice_123
# ✓ age: 25
# ✓ email: alice@example.com
# Now with bad data
my %bad-user = :username<admin>, :age(-5), :email<invalid>;
my $bad-result = validate(%bad-user, $username-rule, $age-rule, $email-rule);
say $bad-result;
# Output:
# Validation Result: FAILED
# ✗ username: 'admin' - Reserved username
# ✗ age: -5 - Must be >= 0
# ✗ email: 'invalid' - Must contain '@'
# Junction-powered "any of these" validation
my $country-rule = Validator.new(
:name<country>,
:checks({ $_ eq any(<US UK CA AU DE FR JP>) })
);
say validate({ country => 'UK' }, $country-rule);
# ✓ country: UK
say validate({ country => 'XX' }, $country-rule);
# ✗ country: 'XX' - Must be one of: US, UK, CA, AU, DE, FR, JP
Implementation Hints:
Junction types and their meaning:
any(1, 2, 3) # True if condition holds for ANY element
all(1, 2, 3) # True if condition holds for ALL elements
one(1, 2, 3) # True if condition holds for EXACTLY ONE element
none(1, 2, 3) # True if condition holds for NO elements
Autothreading example:
my $j = any(1, 2, 3);
say $j * 2; # any(2, 4, 6) - operation distributed!
say $j == 2; # True (because 2 == 2)
say $j == 5; # False (none match)
Questions to guide you:
- What happens when you nest junctions?
any(1, 2) | all(3, 4) - How do you extract individual eigenvalues from a junction?
- When does a junction “collapse” to a boolean?
- How can you use junctions for set operations?
Learning milestones:
- You understand the four junction types → You grasp boolean superposition
- Autothreading works as expected → You understand operation distribution
- Validation rules compose cleanly → You can build practical tools
- Edge cases don’t surprise you → You understand junction semantics
Project 4: Concurrent File Processor with Promises
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Raku concurrency-specific)
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 2: Intermediate
- Knowledge Area: Concurrency / Async
- Software or Tool: Raku Promises
- Main Book: “Raku Fundamentals” by Moritz Lenz
What you’ll build: A parallel file processing tool that uses Promises and Channels to process multiple files concurrently. It will hash files, count lines, search for patterns, or transform content—all in parallel with proper error handling.
Why it teaches Raku: Raku’s concurrency model (Promises, Channels, Supplies) is elegant and composable. Unlike callback-hell or complex async/await, Raku makes concurrent code readable and safe.
Core challenges you’ll face:
- Understanding Promise lifecycle → maps to Planned, Kept, Broken states
- Using Channels for work distribution → maps to producer-consumer patterns
- Combining Promises with await → maps to synchronization
- Handling errors in concurrent code → maps to Promise.broken and .cause
Key Concepts:
- Promises: Promise Documentation - Official Docs
- Channels: Channel Documentation - Official Docs
- Concurrency Overview: Concurrency Guide - Official Docs
- Practical Concurrency: Introducing Async in Raku
Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Project 1, basic understanding of concurrency concepts
Real world outcome:
$ raku file-processor.raku hash --algorithm=sha256 --parallel=4 ./documents/
╔════════════════════════════════════════════════════════════════╗
║ Parallel File Processor v1.0 ║
╚════════════════════════════════════════════════════════════════╝
Operation: SHA256 hash
Directory: ./documents/
Workers: 4
Files found: 156
Processing... [████████████████████░░░░░░░░░░] 67% (105/156)
Worker status:
Worker 1: Processing report_2024.pdf (2.3 MB)
Worker 2: Processing data.csv (45 KB)
Worker 3: Idle (waiting for work)
Worker 4: Processing image_large.png (8.1 MB)
Completed: 100% (156/156)
Time: 4.2 seconds
Throughput: 37.1 files/sec
Results:
report_2024.pdf sha256:a3f2b8c9d4e5f6a7b8c9d0e1f2a3b4c5...
data.csv sha256:1234567890abcdef1234567890abcdef...
image_large.png sha256:fedcba0987654321fedcba0987654321...
...
$ raku file-processor.raku search --pattern="TODO" --parallel=8 ./src/
Searching for: /TODO/
Directory: ./src/
Workers: 8
Results (23 matches in 12 files):
src/main.raku:45 # TODO: Add error handling
src/main.raku:89 # TODO: Optimize this loop
src/parser.raku:12 # TODO: Support Unicode
src/parser.raku:78 # TODO: Add tests
...
$ raku file-processor.raku transform --operation=uppercase --parallel=4 ./input/ ./output/
Transforming files...
input/file1.txt → output/file1.txt (UPPERCASED)
input/file2.txt → output/file2.txt (UPPERCASED)
...
Errors (2 files):
input/binary.dat: Not a text file (skipped)
input/locked.txt: Permission denied
Implementation Hints:
Promise basics:
# Create a promise that runs code in another thread
my $p = start {
sleep 1;
return "Done!";
};
# Wait for it
my $result = await $p;
say $result; # "Done!"
Channel for work distribution:
my $work-channel = Channel.new;
my @workers;
# Create workers
for 1..4 -> $id {
@workers.push: start {
loop {
my $file = $work-channel.receive;
last if $file === Channel::Close;
process-file($file);
}
};
}
# Feed work
for @files -> $file {
$work-channel.send($file);
}
# Signal completion
$work-channel.close;
# Wait for all workers
await @workers;
Questions to guide you:
- What happens if a Promise throws an exception?
- How do you handle timeouts with Promise.in?
- What’s the difference between
awaitand.result? - How do you limit concurrency (not spawn too many threads)?
Learning milestones:
- Promises run code in parallel → You understand basic async
- Channels coordinate workers → You understand work distribution
- Errors are handled gracefully → You understand Promise.broken
- System resources aren’t exhausted → You understand concurrency limits
Project 5: Reactive Event Stream with Supplies
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Supply-specific)
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 3: Advanced
- Knowledge Area: Reactive Programming / Streams
- Software or Tool: Raku Supplies
- Main Book: “Raku Fundamentals” by Moritz Lenz
What you’ll build: A reactive log monitoring system that uses Supplies to watch multiple log files, filter events, aggregate statistics, and trigger alerts—all in real-time with a reactive, push-based architecture.
Why it teaches Raku: Supplies are Raku’s reactive streams—like RxJS or Reactor, but built into the language. Understanding Supplies unlocks event-driven and reactive programming patterns that scale elegantly.
Core challenges you’ll face:
- Understanding live vs on-demand Supplies → maps to hot vs cold observables
- Using supply/react/whenever blocks → maps to reactive composition
- Transforming streams (grep, map, batch) → maps to stream operators
- Combining multiple Supplies → maps to merging event sources
Key Concepts:
- Supply Basics: Supply Documentation - Official Docs
- React/Whenever: Concurrency Guide - Official Docs
- Reactive Patterns: Supplies Reactive Programming
- File Watching: IO::Notification
Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Project 4, understanding of reactive/event-driven concepts
Real world outcome:
$ raku log-monitor.raku --config monitor.yaml
╔════════════════════════════════════════════════════════════════╗
║ Reactive Log Monitor v1.0 ║
╚════════════════════════════════════════════════════════════════╝
Configuration loaded:
Watching: /var/log/app/*.log (3 files)
Filters:
- ERROR → Alert (Slack #alerts)
- WARN → Aggregate (5-minute window)
- INFO → Count only
Starting reactive streams...
[LIVE STREAM]
14:23:01 app.log INFO User alice logged in
14:23:02 api.log INFO GET /api/users 200 45ms
14:23:03 app.log WARN Slow query: 2.3s (threshold: 1s)
14:23:05 api.log ERROR Connection timeout to database
⚠️ ALERT TRIGGERED → Sending to Slack...
✓ Alert sent to #alerts
[STATISTICS - Rolling 5 minutes]
┌─────────────┬───────┬───────┬───────┐
│ File │ INFO │ WARN │ ERROR │
├─────────────┼───────┼───────┼───────┤
│ app.log │ 423 │ 12 │ 0 │
│ api.log │ 891 │ 3 │ 1 │
│ worker.log │ 156 │ 0 │ 0 │
└─────────────┴───────┴───────┴───────┘
Events/sec: 24.5 (avg), 89 (peak)
Memory: 45 MB
Uptime: 2h 34m
Press Ctrl+C to stop...
^C
Shutting down gracefully...
Flushing statistics to disk...
Closing file handles...
Done.
Implementation Hints:
Supply basics:
# On-demand supply (like a function that returns values over time)
my $numbers = supply {
for 1..10 {
emit $_;
sleep 0.1;
}
}
# Live supply (like an event emitter)
my $events = Supplier.new;
my $live-supply = $events.Supply;
# Emit events
$events.emit("Something happened!");
React/whenever for combining streams:
react {
whenever $log-file-supply -> $line {
process-log-line($line);
}
whenever Supply.interval(60) {
print-statistics();
}
whenever signal(SIGINT) {
say "Shutting down...";
done; # Exit the react block
}
}
Stream operators:
$log-supply
.grep(*.contains('ERROR')) # Filter
.map({ parse-log-line($_) }) # Transform
.batch(:seconds(60)) # Aggregate by time
.tap({ send-alert($_) }); # Act on each batch
Questions to guide you:
- What’s the difference between
tapandreact/whenever? - How do you handle backpressure (slow consumer)?
- What happens when a Supply throws an exception?
- How do you test reactive code?
Learning milestones:
- File changes trigger events → You understand IO::Notification
- Multiple streams combine smoothly → You understand whenever
- Aggregation works over time windows → You understand batch
- Graceful shutdown works → You understand done and quit
Project 6: Multi-Dispatch Calculator
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Multi-dispatch specific)
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 2: Intermediate
- Knowledge Area: Type System / Dispatch
- Software or Tool: Raku Core
- Main Book: “Raku Fundamentals” by Moritz Lenz
What you’ll build: An extensible calculator that uses multi-dispatch to handle different types (integers, rationals, complex, matrices, units) with appropriate precision and operations. Add new types without modifying existing code.
Why it teaches Raku: Multi-dispatch is Raku’s approach to polymorphism—more powerful than single dispatch (methods), more flexible than function overloading. Understanding it reveals how Raku achieves expressiveness without complexity.
Core challenges you’ll face:
- Defining multi candidates → maps to signature-based dispatch
- Type constraints and where clauses → maps to narrowing candidates
- Dispatch order and specificity → maps to which candidate wins
- Literal values in signatures → maps to pattern matching on values
Key Concepts:
- Multi Dispatch: Multi Documentation - Official Docs
- Signatures: Signature Literals - Official Docs
- Type Constraints: “Raku Fundamentals” Chapter 4 - Moritz Lenz
- Where Clauses: Functions Guide - Official Docs
Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Project 1, basic understanding of types and polymorphism
Real world outcome:
$ raku calculator.raku
╔════════════════════════════════════════════════════════════════╗
║ Multi-Dispatch Calculator v1.0 ║
╚════════════════════════════════════════════════════════════════╝
> 5 + 3
= 8 (Int)
> 1/3 + 1/6
= 1/2 (Rat)
Exact rational arithmetic, no floating point errors!
> 2 + 3i + (1 - 2i)
= 3+1i (Complex)
> 2m + 50cm
= 2.5m (Length)
Automatic unit conversion!
> 100km/h * 2h
= 200km (Distance)
Dimensional analysis!
> [[1,2],[3,4]] + [[5,6],[7,8]]
= [[6,8],[10,12]] (Matrix)
> [[1,2],[3,4]] * [[5,6],[7,8]]
= [[19,22],[43,50]] (Matrix multiplication)
> factorial(5)
= 120
> factorial(100)
= 93326215443944152681699238856266700490715968264381621468592963...
(Arbitrary precision!)
> factorial(-1)
Error: factorial requires non-negative integer
> 0 / 0
= NaN (Not a Number - handled gracefully)
> "hello" + "world"
= "helloworld" (String concatenation)
> [1,2,3] + [4,5,6]
= [5,7,9] (Element-wise addition)
:types
Registered types:
Int, Rat, Num, Complex, Str, Length, Duration, Matrix, ...
:add-type
Define new type interactively...
Implementation Hints:
Multi-dispatch basics:
# Dispatch on type
multi sub calculate(Int $a, '+', Int $b) { $a + $b }
multi sub calculate(Rat $a, '+', Rat $b) { $a + $b }
multi sub calculate(Complex $a, '+', Complex $b) { $a + $b }
# Dispatch on literal values
multi sub factorial(0) { 1 }
multi sub factorial(Int $n where * > 0) { $n * factorial($n - 1) }
multi sub factorial(Int $n where * < 0) { die "Negative factorial!" }
# Dispatch with where clauses
multi sub divide($a, $b where * != 0) { $a / $b }
multi sub divide($a, 0) { NaN }
Type hierarchy affects dispatch:
Most specific wins:
calculate(Int, '+', Int) # Wins for (5, '+', 3)
calculate(Numeric, '+', Numeric) # Fallback
Specificity order:
1. Literal values (42, 'foo')
2. Constrained types (Int where * > 0)
3. Concrete types (Int, Str)
4. Roles (Numeric, Stringy)
5. Any
Questions to guide you:
- What happens when two candidates are equally specific?
- How do you call a specific candidate explicitly?
- Can you dispatch on return type? (Hint: No, but see
returns) - How do you debug dispatch failures?
Learning milestones:
- Basic type dispatch works → You understand signatures
- Where clauses constrain correctly → You understand narrowing
- New types integrate seamlessly → You understand extensibility
- Edge cases dispatch correctly → You understand specificity rules
Project 7: Role-Based Plugin System
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Raku roles specific)
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 3: Advanced
- Knowledge Area: OOP / Composition
- Software or Tool: Raku Roles
- Main Book: “Raku Fundamentals” by Moritz Lenz
What you’ll build: A plugin system where plugins are defined as Roles that can be composed into a host application at compile-time or mixed in at runtime. Create a text editor core with syntax highlighting, auto-save, and spell-check as composable plugins.
Why it teaches Raku: Roles are Raku’s answer to the fragile base class problem and multiple inheritance complexity. They enable safe, flexible composition—a pattern increasingly popular in modern languages.
Core challenges you’ll face:
- Defining roles with methods and attributes → maps to interface + implementation
- Compile-time vs runtime composition → maps to does vs but
- Conflict resolution → maps to what happens when roles collide
- Parameterized roles → maps to generic programming with roles
Key Concepts:
- Roles: Role Documentation - Official Docs
- Composition: Object Orientation - Official Docs
- Mixins: Metamodel::Mixins - Official Docs
- Design Spec: Synopsis 14
Difficulty: Advanced Time estimate: 2 weeks Prerequisites: Project 1, understanding of OOP concepts, interfaces/traits in other languages
Real world outcome:
$ raku plugin-editor.raku
╔════════════════════════════════════════════════════════════════╗
║ Plugin-Based Text Editor v1.0 ║
╚════════════════════════════════════════════════════════════════╝
Core editor started.
Available plugins: SyntaxHighlight, AutoSave, SpellCheck, GitIntegration
:load SyntaxHighlight
✓ SyntaxHighlight loaded
Supports: Raku, Python, JavaScript, Rust
:load AutoSave
✓ AutoSave loaded
Interval: 30 seconds
Location: ~/.editor/autosave/
:load SpellCheck
✓ SpellCheck loaded
Dictionary: en_US (143,091 words)
:status
Editor with plugins:
Core: TextBuffer, Cursor, Undo/Redo
Plugins:
- SyntaxHighlight (highlighting 'Raku')
- AutoSave (next save in 25s)
- SpellCheck (2 misspellings found)
:edit sample.raku
[Line 1] my $greeting = "Helo World";
^^^^
SpellCheck: "Helo" → Did you mean "Hello"?
[Line 2] say $greeting;
^^^ SyntaxHighlight: keyword (blue)
^^^^^^^^^^ SyntaxHighlight: variable (green)
:unload SpellCheck
✓ SpellCheck unloaded
Note: Runtime mixin removed, core functionality intact.
:save
✓ Saved to sample.raku
AutoSave timer reset.
# Hot-reload a plugin with new version
:reload SyntaxHighlight
✓ SyntaxHighlight reloaded (v1.0 → v1.1)
New: Added Raku Grammar highlighting
Implementation Hints:
Role definition:
role SyntaxHighlight {
has %.syntax-rules;
method highlight(Str $text --> Str) {
# Apply highlighting
...
}
method supported-languages() {
<Raku Python JavaScript Rust>
}
}
role AutoSave {
has Int $.interval = 30;
has $.autosave-path;
method start-autosave() {
start {
loop {
sleep $.interval;
self.save-backup();
}
}
}
method save-backup() { ... }
}
Compile-time composition:
class Editor does SyntaxHighlight does AutoSave {
# Gets all methods and attributes from both roles
}
Runtime mixin:
my $editor = Editor.new;
$editor does SpellCheck; # Add SpellCheck at runtime!
Conflict resolution:
role A { method foo() { "A" } }
role B { method foo() { "B" } }
class C does A does B {
# Compile error! Must resolve conflict:
method foo() { self.A::foo() ~ self.B::foo() }
}
Questions to guide you:
- What’s the difference between
doesandiswith roles? - How do you require methods without implementing them?
- Can you apply a role to a single object, not a class?
- How do parameterized roles work?
role Foo[Type $t]
Learning milestones:
- Roles compose at compile-time → You understand static composition
- Runtime mixins work → You understand dynamic composition
- Conflicts are resolved correctly → You understand the diamond problem solution
- Parameterized roles add flexibility → You understand generic roles
Project 8: MOP-Powered ORM (Object-Relational Mapper)
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (MOP-specific)
- Coolness Level: Level 5: Pure Magic (Super Cool)
- Business Potential: 4. The “Open Core” Infrastructure
- Difficulty: Level 4: Expert
- Knowledge Area: Metaprogramming / MOP
- Software or Tool: Raku Meta-Object Protocol
- Main Book: “Raku Fundamentals” by Moritz Lenz
What you’ll build: A simple ORM that uses the Meta-Object Protocol to automatically map classes to database tables, generate SQL, and handle relationships—all by introspecting class definitions at runtime.
Why it teaches Raku: The MOP is Raku’s most powerful (and least understood) feature. It lets you treat classes, methods, and attributes as objects you can inspect and modify. This is metaprogramming at its purest.
Core challenges you’ll face:
- Understanding .HOW and metaclasses → maps to classes are objects too
- Introspecting attributes and methods → maps to .^attributes, .^methods
- Creating classes programmatically → maps to Metamodel::ClassHOW
- Modifying classes at runtime → maps to adding methods dynamically
Key Concepts:
- MOP Basics: Meta-Object Protocol - Official Docs
- Introspection: Advent Calendar: Introspection
- Metamodel Deep Dive: Vrurg’s MOP Series
- Class Creation: Metamodel::ClassHOW
Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: Projects 1-7, understanding of ORMs, basic SQL
Real world outcome:
$ raku orm-demo.raku
use MiniORM;
# Define a model - the ORM introspects this class
class User is Model {
has Int $.id is primary-key is auto-increment;
has Str $.name is column is required;
has Str $.email is column is unique;
has DateTime $.created-at is column = DateTime.now;
}
class Post is Model {
has Int $.id is primary-key is auto-increment;
has Str $.title is column is required;
has Str $.body is column;
has Int $.user-id is foreign-key(User);
}
# ORM automatically generates SQL
say User.create-table-sql;
# CREATE TABLE users (
# id INTEGER PRIMARY KEY AUTOINCREMENT,
# name TEXT NOT NULL,
# email TEXT UNIQUE,
# created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
# );
# Create records
my $user = User.create(name => 'Alice', email => 'alice@example.com');
say $user.id; # 1 (auto-generated)
# Query
my @users = User.where: *.email.contains('@example.com');
say @users.elems; # 1
# Relationships (auto-discovered via foreign keys)
my $post = Post.create(
title => 'Hello World',
body => 'My first post',
user-id => $user.id
);
say $post.user.name; # "Alice" - automatic relationship resolution!
# Inspect the ORM's understanding of your model
say User.^orm-columns;
# (id => {type => Int, primary-key => True, auto-increment => True},
# name => {type => Str, required => True},
# email => {type => Str, unique => True},
# created-at => {type => DateTime, default => ...})
# Dynamic method generation
say User.^methods.grep(*.name eq 'find-by-email');
# (Method: find-by-email) - Auto-generated!
$user = User.find-by-email('alice@example.com');
Implementation Hints:
MOP introspection basics:
class Foo {
has Int $.x;
has Str $.y;
method greet() { say "Hello" }
}
# Get the metaclass
say Foo.HOW; # Perl6::Metamodel::ClassHOW
say Foo.^name; # "Foo" (shortcut for .HOW.name)
# Introspect attributes
for Foo.^attributes -> $attr {
say $attr.name; # $!x, $!y
say $attr.type; # (Int), (Str)
say $attr.has_accessor; # True (because of the . twigil)
}
# Introspect methods
for Foo.^methods(:local) -> $method {
say $method.name; # "greet"
}
Custom traits (for is column, is primary-key):
multi trait_mod:<is>(Attribute $attr, :$column!) {
# Mark this attribute as a database column
$attr does ColumnTrait;
}
multi trait_mod:<is>(Attribute $attr, :$primary-key!) {
$attr does PrimaryKeyTrait;
}
Dynamic method addition:
# Add a method to a class at runtime
User.^add_method('find-by-email', method (Str $email) {
self.where(*.email eq $email).first
});
User.^compose; # Finalize changes
Questions to guide you:
- What’s the difference between
.^methodsand.^methods(:local)? - How do you access private attributes via the MOP?
- Can you remove methods from a class? Add attributes?
- How do traits interact with the MOP?
Learning milestones:
- You can introspect any class → You understand .^attributes, .^methods
- Custom traits mark attributes → You understand trait_mod
- Methods are generated dynamically → You understand .^add_method
- The ORM works end-to-end → You’ve combined all MOP concepts
Project 9: Domain-Specific Language (DSL) Creator
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Raku DSL specific)
- Coolness Level: Level 5: Pure Magic (Super Cool)
- Business Potential: 4. The “Open Core” Infrastructure
- Difficulty: Level 4: Expert
- Knowledge Area: Language Design / DSLs
- Software or Tool: Raku Grammars + Slang
- Main Book: “Raku Fundamentals” by Moritz Lenz
What you’ll build: A framework for creating domain-specific languages that compile to Raku. Create a simple DSL (like a build tool configuration, test specification, or workflow definition) that feels natural to domain experts but executes as Raku code.
Why it teaches Raku: Raku was designed for DSL creation. Grammars handle parsing, slangs modify the parser itself, and the MOP allows seamless integration. This is where all of Raku’s features come together.
Core challenges you’ll face:
- Designing a DSL syntax → maps to grammar design
- Compiling DSL to Raku AST → maps to actions and code generation
- Providing good error messages → maps to parse failure handling
- Integrating with Raku seamlessly → maps to the slang system
Key Concepts:
- Grammar Actions: Grammar Tutorial - Official Docs
- AST Manipulation: Q (AST) Documentation
- Slangs: Advent Calendar on Slangs
- DSL Design: “Raku Fundamentals” Chapter 8 - Moritz Lenz
Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: Projects 2 and 8, understanding of language design concepts
Real world outcome:
$ cat build.spec
# A simple build DSL
project "MyApp" version "1.0.0"
dependencies:
- raku >= 2024.01
- HTTP::UserAgent
- JSON::Fast
tasks:
test:
description "Run all tests"
run "prove6 -l t/"
build:
description "Build the application"
depends-on test
run:
- "raku --doc=Markdown lib/MyApp.rakumod > README.md"
- "raku -c lib/MyApp.rakumod"
deploy:
description "Deploy to production"
depends-on build
environment production
run:
- "rsync -av lib/ server:/app/lib/"
- "ssh server 'systemctl restart myapp'"
$ raku dsl-builder.raku build.spec --task=build
╔════════════════════════════════════════════════════════════════╗
║ Build System - MyApp v1.0.0 ║
╚════════════════════════════════════════════════════════════════╝
Parsing build.spec...
✓ Valid build specification
Resolving task: build
Dependencies: test → build
Running: test
Description: Run all tests
$ prove6 -l t/
t/basic.t .... ok
All tests successful.
Running: build
Description: Build the application
$ raku --doc=Markdown lib/MyApp.rakumod > README.md
$ raku -c lib/MyApp.rakumod
Syntax OK
✓ Task 'build' completed successfully
# Show what the DSL compiled to
$ raku dsl-builder.raku build.spec --show-compiled
Compiled Raku code:
```raku
class Build {
has $.project = "MyApp";
has $.version = "1.0.0";
method task-test() {
shell "prove6 -l t/"
}
method task-build() {
self.task-test();
shell "raku --doc=Markdown lib/MyApp.rakumod > README.md";
shell "raku -c lib/MyApp.rakumod";
}
...
}
**Implementation Hints**:
DSL grammar:
```raku
grammar BuildSpec {
rule TOP { <header> <section>* }
rule header {
'project' <string> 'version' <string>
}
rule section {
| <dependencies>
| <tasks>
}
rule tasks {
'tasks:' <task>+
}
rule task {
<identifier> ':'
<task-property>+
}
rule task-property {
| 'description' <string>
| 'depends-on' <identifier>
| 'run' <command-or-list>
}
token string { '"' <-["]>* '"' }
token identifier { <[\w-]>+ }
}
Compiling to Raku code:
class BuildSpec::Actions {
method TOP($/) {
make Q:c:to/RAKU/;
class Build \{
{$<header>.made}
{$<section>».made.join("\n")}
\}
RAKU
}
method task($/) {
my $name = ~$<identifier>;
my $body = $<task-property>».made.join(";\n");
make qq:to/RAKU/;
method task-$name() \{
$body
\}
RAKU
}
}
Questions to guide you:
- How do you provide line/column numbers in error messages?
- How do you handle DSL escape hatches (embedding raw Raku)?
- What makes a DSL feel “natural” vs “just weird syntax”?
- How do you test a DSL compiler?
Learning milestones:
- Grammar parses the DSL correctly → You understand grammar design
- Actions generate valid Raku code → You understand compilation
- Error messages are helpful → You understand user experience
- The DSL executes correctly → You’ve built a complete compiler
Project 10: Programming Language Implementation
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Raku implementation)
- Coolness Level: Level 5: Pure Magic (Super Cool)
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 5: Master
- Knowledge Area: Language Implementation / Interpreters
- Software or Tool: Raku Grammars + MOP
- Main Book: “Language Implementation Patterns” by Terence Parr
What you’ll build: A complete interpreter for a small programming language (like a Lisp variant, a stack-based language, or a simple imperative language) using Raku’s grammars for parsing, the MOP for the runtime, and Supplies for I/O.
Why it teaches Raku: This is the ultimate Raku project—it uses everything: grammars for parsing, actions for AST building, classes for the runtime, multi-dispatch for the evaluator, and the MOP for reflection. You’ll understand Raku deeply by implementing a language in it.
Core challenges you’ll face:
- Designing the language → maps to syntax and semantics
- Implementing the parser → maps to grammars and AST
- Building the evaluator → maps to tree-walking interpretation
- Handling scope and closures → maps to environment management
- Error handling and debugging → maps to source mapping
Key Concepts:
- Interpreter Patterns: “Language Implementation Patterns” - Terence Parr
- Grammar Design: Raku Grammars - Official Docs
- Evaluation: “Crafting Interpreters” - Bob Nystrom (Web)
- Closures: “Structure and Interpretation of Computer Programs” - Abelson & Sussman
Difficulty: Master Time estimate: 4-6 weeks Prerequisites: All previous projects, understanding of interpreters/compilers
Real world outcome:
$ cat factorial.mini
# A simple language: MiniLang
fn factorial(n) {
if n <= 1 {
return 1
}
return n * factorial(n - 1)
}
fn main() {
let result = factorial(10)
print("10! = " + str(result))
}
main()
$ raku minilang.raku factorial.mini
╔════════════════════════════════════════════════════════════════╗
║ MiniLang Interpreter v1.0 ║
╚════════════════════════════════════════════════════════════════╝
Parsing factorial.mini...
✓ Parse successful
AST:
Program
├─ FnDef: factorial(n)
│ └─ If
│ ├─ Condition: n <= 1
│ ├─ Then: Return(1)
│ └─ Else: Return(n * factorial(n - 1))
├─ FnDef: main()
│ ├─ Let: result = factorial(10)
│ └─ Call: print(...)
└─ Call: main()
Executing...
10! = 3628800
$ raku minilang.raku --repl
MiniLang REPL (type 'exit' to quit)
>>> let x = 5
5
>>> x * 2
10
>>> fn double(n) { return n * 2 }
<function double>
>>> double(x)
10
>>> let add = fn(a, b) { return a + b } # Lambda!
<function>
>>> add(3, 4)
7
>>> exit
$ raku minilang.raku factorial.mini --trace
TRACE: Entering main()
TRACE: Evaluating: factorial(10)
TRACE: Entering factorial(n=10)
TRACE: Condition: 10 <= 1 = False
TRACE: Evaluating: 10 * factorial(9)
TRACE: Entering factorial(n=9)
...
TRACE: factorial(1) returned 1
TRACE: factorial(2) returned 2
...
TRACE: factorial(10) returned 3628800
TRACE: print called with: "10! = 3628800"
10! = 3628800
Implementation Hints:
Grammar for MiniLang:
grammar MiniLang {
rule TOP { <statement>* }
proto rule statement {*}
rule statement:sym<fn> { 'fn' <identifier> '(' <params> ')' <block> }
rule statement:sym<let> { 'let' <identifier> '=' <expr> }
rule statement:sym<if> { 'if' <expr> <block> ['else' <block>]? }
rule statement:sym<return>{ 'return' <expr> }
rule statement:sym<expr> { <expr> }
rule expr { <term> [<binop> <term>]* }
rule term { <factor> [<mulop> <factor>]* }
proto token factor {*}
token factor:sym<num> { \d+ }
token factor:sym<str> { '"' <-["]>* '"' }
token factor:sym<ident> { <identifier> }
token factor:sym<call> { <identifier> '(' <args> ')' }
token factor:sym<paren> { '(' <expr> ')' }
token factor:sym<lambda>{ 'fn' '(' <params> ')' <block> }
}
Tree-walking evaluator with multi-dispatch:
class Evaluator {
has %.env; # Variable environment
multi method eval(NumLiteral $node) { $node.value }
multi method eval(StrLiteral $node) { $node.value }
multi method eval(Identifier $node) { %.env{$node.name} }
multi method eval(BinOp $node) {
my $left = self.eval($node.left);
my $right = self.eval($node.right);
given $node.op {
when '+' { $left + $right }
when '-' { $left - $right }
when '*' { $left * $right }
when '/' { $left / $right }
when '<=' { $left <= $right }
}
}
multi method eval(FnCall $node) {
my $fn = %.env{$node.name};
my @args = $node.args.map: { self.eval($_) };
self.call-function($fn, @args);
}
multi method eval(IfStmt $node) {
if self.eval($node.condition) {
self.eval($node.then-block)
} else {
self.eval($node.else-block) if $node.else-block
}
}
}
Questions to guide you:
- How do you implement closures? (Hint: capture the environment)
- How do you handle recursion in the evaluator?
- What’s the difference between an AST and a parse tree?
- How do you report errors with line numbers?
Learning milestones:
- Parser handles all syntax → You understand grammar design
- Basic expressions evaluate correctly → You understand tree-walking
- Functions and closures work → You understand scope and environments
- REPL provides instant feedback → You’ve built an interactive tool
Project 11: Unicode Text Processing Toolkit
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Raku Unicode-specific)
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 2. The “Micro-SaaS / Pro Tool”
- Difficulty: Level 2: Intermediate
- Knowledge Area: Unicode / Text Processing
- Software or Tool: Raku Unicode Support
- Main Book: “Using Raku” by Andrew Shitov
What you’ll build: A text processing toolkit that properly handles Unicode: grapheme-based string operations, normalization, script detection, bidirectional text, emoji handling, and transliteration. Raku’s Unicode-first design makes this elegant.
Why it teaches Raku: Most languages treat strings as bytes or code points. Raku treats them as graphemes (what humans perceive as characters). This project reveals Raku’s sophisticated Unicode support and why it matters.
Core challenges you’ll face:
- Understanding graphemes vs codepoints → maps to why “é” can be 1 or 2 codepoints
- Normalization forms (NFC, NFD) → maps to canonical equivalence
- Script and category detection → maps to Unicode properties
- Handling emoji and ZWJ sequences → maps to modern Unicode complexity
Key Concepts:
- Raku Unicode: Unicode in Raku - Official Docs
- Grapheme Clusters: Uni Documentation - Official Docs
- Unicode Standard: Unicode Technical Reports
- Text Processing: “Using Raku” - Andrew Shitov
Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Project 1, basic understanding of Unicode concepts
Real world outcome:
$ raku unicode-toolkit.raku
╔════════════════════════════════════════════════════════════════╗
║ Unicode Text Toolkit v1.0 ║
╚════════════════════════════════════════════════════════════════╝
> analyze "Héllo 世界 👨👩👧👦"
String Analysis:
Display: Héllo 世界 👨👩👧👦
Graphemes: 11
Codepoints: 18
Bytes (UTF-8): 29
Breakdown:
│ Grapheme │ Codepoints │ Name(s) │
├──────────┼────────────┼──────────────────────────────────────┤
│ H │ 1 │ LATIN CAPITAL LETTER H │
│ é │ 1 │ LATIN SMALL LETTER E WITH ACUTE │
│ l │ 1 │ LATIN SMALL LETTER L │
│ l │ 1 │ LATIN SMALL LETTER L │
│ o │ 1 │ LATIN SMALL LETTER O │
│ (space) │ 1 │ SPACE │
│ 世 │ 1 │ CJK UNIFIED IDEOGRAPH-4E16 │
│ 界 │ 1 │ CJK UNIFIED IDEOGRAPH-754C │
│ (space) │ 1 │ SPACE │
│ 👨👩👧👦 │ 7 │ Family (Man, Woman, Girl, Boy) │
│ │ │ ZWJ sequence │
└──────────┴────────────┴──────────────────────────────────────┘
Scripts detected: Latin, Han, Common
> normalize "café" --form=NFD
Original: café (5 graphemes, 5 codepoints)
NFD: café (5 graphemes, 6 codepoints)
The 'é' was decomposed to 'e' + COMBINING ACUTE ACCENT
> compare "café" "café"
String 1: café (NFC form)
String 2: café (NFD form - contains combining characters)
Visually identical: Yes
Byte-equal: No
Canonically equivalent: Yes
> transliterate "Москва" --to=Latin
Original: Москва (Cyrillic)
Result: Moskva (Latin)
> detect-script "こんにちは世界"
Scripts found:
- Hiragana: こんにちは (5 characters)
- Han: 世界 (2 characters)
> reverse "Hello 👨👩👧"
Naive byte reverse: ����👨 olleH ← WRONG!
Grapheme reverse: 👨👩👧 olleH ← CORRECT!
Raku handles this correctly by default.
Implementation Hints:
Raku’s Unicode-aware operations:
my $str = "👨👩👧👦";
say $str.chars; # 1 (graphemes - correct!)
say $str.codes; # 7 (codepoints)
say $str.encode.bytes; # 25 (UTF-8 bytes)
# Grapheme-aware operations
say $str.comb; # (👨👩👧👦) - one element
say $str.ords; # (128104 8205 128105 8205 128103 8205 128102)
# Unicode properties
say "A".uniprop; # Lu (Letter, uppercase)
say "5".uniprop; # Nd (Number, decimal digit)
say "é".NFD.codes; # 2 (e + combining acute)
Normalization:
my $nfc = "é"; # Single codepoint (U+00E9)
my $nfd = "é".NFD; # Two codepoints (e + U+0301)
say $nfc eq $nfd; # False (different codepoints)
say $nfc.NFC eqv $nfd.NFC; # True (canonically equivalent)
Questions to guide you:
- Why does
"café".charsreturn 4, but"café".codesmight return 5? - How do you detect if a string needs normalization?
- What’s a ZWJ (Zero Width Joiner) and why do emoji use it?
- How do you properly reverse a Unicode string?
Learning milestones:
- Grapheme operations work correctly → You understand Raku’s string model
- Normalization handles edge cases → You understand canonical equivalence
- Emoji and complex scripts work → You understand modern Unicode
- Toolkit handles real-world text → You can process any language
Project 12: Raku Module Publishing Workflow
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Alternative Programming Languages: N/A (Raku ecosystem)
- Coolness Level: Level 2: Practical but Forgettable
- Business Potential: 2. The “Micro-SaaS / Pro Tool”
- Difficulty: Level 2: Intermediate
- Knowledge Area: Package Management / Ecosystem
- Software or Tool: zef, fez, Raku ecosystem
- Main Book: “Raku Fundamentals” by Moritz Lenz
What you’ll build: Create, test, document, and publish a real Raku module to the ecosystem. Along the way, build a local development workflow with proper testing, documentation generation, and continuous integration.
Why it teaches Raku: Contributing to the ecosystem cements your learning. You’ll encounter real-world Raku patterns, learn from code review, and become part of the community. This is how you go from learner to contributor.
Core challenges you’ll face:
- Module structure and META6.json → maps to package metadata
- Writing tests with Test module → maps to Raku testing patterns
- Documentation with Pod6 → maps to Raku’s documentation system
- Publishing with fez → maps to ecosystem contribution
Key Concepts:
- Module Structure: Module Development - Official Docs
- Testing: Test Module - Official Docs
- Pod6 Documentation: Pod6 - Official Docs
- Publishing: fez - Package uploader
Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Several previous projects completed, basic git knowledge
Real world outcome:
$ mkdir MyModule && cd MyModule
$ raku-module-init
Creating module structure...
lib/MyModule.rakumod
t/01-basic.rakutest
META6.json
README.md
.github/workflows/test.yml
$ tree
.
├── META6.json
├── README.md
├── lib
│ └── MyModule.rakumod
├── t
│ └── 01-basic.rakutest
└── .github
└── workflows
└── test.yml
# Write your module
$ cat lib/MyModule.rakumod
unit module MyModule;
sub greet(Str $name --> Str) is export {
"Hello, $name!"
}
# Write tests
$ cat t/01-basic.rakutest
use Test;
use MyModule;
plan 3;
is greet("World"), "Hello, World!", "Basic greeting works";
is greet("Raku"), "Hello, Raku!", "Name substitution works";
dies-ok { greet(42) }, "Type check works";
# Run tests
$ prove6 -l
t/01-basic.rakutest .. ok
All tests successful.
# Generate documentation
$ raku --doc=Markdown lib/MyModule.rakumod > docs/API.md
# Publish to ecosystem
$ fez register # First time only
$ fez upload
Uploading MyModule:ver<0.0.1>...
✓ Upload successful!
Your module is now available at:
https://raku.land/zef:yourname/MyModule
Install with: zef install MyModule
Implementation Hints:
META6.json structure:
{
"name": "MyModule",
"version": "0.0.1",
"auth": "zef:yourname",
"api": "1",
"description": "A brief description",
"depends": [],
"provides": {
"MyModule": "lib/MyModule.rakumod"
},
"license": "Artistic-2.0",
"source-url": "https://github.com/yourname/MyModule.git"
}
Pod6 documentation in module:
#| Greet someone by name
#| Returns a greeting string
sub greet(
Str $name #= The name to greet
--> Str
) is export {
"Hello, $name!"
}
=begin pod
=head1 NAME
MyModule - A friendly greeting module
=head1 SYNOPSIS
use MyModule;
say greet("World"); # Hello, World!
=head1 DESCRIPTION
This module provides greeting functionality.
=end pod
Questions to guide you:
- What goes in
providesvsdependsin META6.json? - How do you test async/concurrent code?
- What’s the difference between
is exportandis export(:tag)? - How do you handle breaking API changes (versioning)?
Learning milestones:
- Module installs locally → You understand the module system
- Tests pass reliably → You understand Raku testing
- Documentation renders nicely → You understand Pod6
- Module is published and usable → You’re a Raku contributor!
Project Comparison Table
| Project | Difficulty | Time | Depth of Understanding | Fun Factor |
|---|---|---|---|---|
| 1. REPL Companion | Beginner | Weekend | ★★★☆☆ | ★★★★☆ |
| 2. Grammar Parser | Intermediate | 1-2 weeks | ★★★★★ | ★★★★★ |
| 3. Junction Validator | Intermediate | 1 week | ★★★★☆ | ★★★★☆ |
| 4. Promise File Processor | Intermediate | 1-2 weeks | ★★★★☆ | ★★★☆☆ |
| 5. Supply Log Monitor | Advanced | 2-3 weeks | ★★★★★ | ★★★★☆ |
| 6. Multi-Dispatch Calculator | Intermediate | 1-2 weeks | ★★★★☆ | ★★★★☆ |
| 7. Role Plugin System | Advanced | 2 weeks | ★★★★☆ | ★★★★☆ |
| 8. MOP-Powered ORM | Expert | 3-4 weeks | ★★★★★ | ★★★★★ |
| 9. DSL Creator | Expert | 3-4 weeks | ★★★★★ | ★★★★★ |
| 10. Language Interpreter | Master | 4-6 weeks | ★★★★★ | ★★★★★ |
| 11. Unicode Toolkit | Intermediate | 1-2 weeks | ★★★★☆ | ★★★☆☆ |
| 12. Module Publishing | Intermediate | 1-2 weeks | ★★★☆☆ | ★★★★☆ |
Recommended Learning Path
If you have 2 weeks (quick taste):
- Project 1: REPL Companion (weekend)
- Project 2: Grammar Parser (1 week)
- Project 3: Junction Validator (3-4 days)
If you have 1-2 months (solid foundation):
- Projects 1-3 (2 weeks)
- Project 4: Promise File Processor (1 week)
- Project 6: Multi-Dispatch Calculator (1 week)
- Project 7: Role Plugin System (1.5 weeks)
- Project 12: Publish a Module (1 week)
If you have 3+ months (Raku mastery):
Complete all projects in order. Each builds on the previous.
Recommended Starting Point
Start with Project 1 (REPL Companion) to get comfortable with Raku’s unique syntax. Then move to Project 2 (Grammar Parser)—grammars are Raku’s killer feature and will hook you on the language.
If you’re coming from:
- Perl: Skip to Project 2, you’ll feel at home
- Python/Ruby: Start with Project 1, the syntax will feel different
- Haskell/ML: You’ll love Projects 3 (Junctions) and 6 (Multi-Dispatch)
- JavaScript/TypeScript: Projects 4-5 (async) will feel familiar
Final Capstone: Full-Stack Raku Application
After completing the individual projects, build a complete application:
- File: LEARN_RAKU_DEEP_DIVE.md
- Main Programming Language: Raku
- Coolness Level: Level 5: Pure Magic (Super Cool)
- Business Potential: 4. The “Open Core” Infrastructure
- Difficulty: Level 5: Master
- Knowledge Area: Full-Stack Raku
What you’ll build: A web application that combines:
- Cro for the web framework
- Grammars for parsing user input
- Supplies for real-time updates (WebSockets)
- Roles for clean architecture
- The MOP for dynamic behavior
- A DSL for configuration
This could be a real-time chat application, a collaborative code editor, a monitoring dashboard, or a game server.
Why it matters: This project proves you can build production-quality software in Raku. It’s a portfolio piece that demonstrates mastery of the language’s unique features.
Summary
| # | Project | Main Language |
|---|---|---|
| 1 | Interactive REPL Companion | Raku |
| 2 | Data Format Parser with Grammars | Raku |
| 3 | Junction-Powered Validation Library | Raku |
| 4 | Concurrent File Processor with Promises | Raku |
| 5 | Reactive Event Stream with Supplies | Raku |
| 6 | Multi-Dispatch Calculator | Raku |
| 7 | Role-Based Plugin System | Raku |
| 8 | MOP-Powered ORM | Raku |
| 9 | Domain-Specific Language Creator | Raku |
| 10 | Programming Language Implementation | Raku |
| 11 | Unicode Text Processing Toolkit | Raku |
| 12 | Raku Module Publishing Workflow | Raku |
| Final | Full-Stack Raku Application | Raku |
Key Resources
Official Resources
- Official Raku Documentation - The authoritative reference
- The Complete Course - Andrew Shitov’s comprehensive tutorial
- Raku Guide - Quick reference
- Exercism Raku Track - 87 exercises with mentoring
Books
- “Raku Fundamentals” by Moritz Lenz - Hands-on introduction with real projects
- “Using Raku” by Andrew Shitov - 100 programming challenges
- “Think Raku” by Laurent Rosenfeld - Free online book
Community
- IRC: #raku on irc.libera.chat
- Discord: Raku Discord
- Reddit: r/rakulang
- Weekly: Raku Weekly
Tools
- Rakudo: The main Raku compiler
- zef: Package manager
- Comma IDE: JetBrains-based IDE for Raku
- VS Code: raku-navigator extension
Sources used in this guide: