Introduction
“Programming the way humans think, not the way computers think”
Welcome to SFX (Situation Framework eXchange) - a beginner-friendly, context-oriented programming language designed for the 2025 AI era. With JIT compilation, reactive observers, and mathematical honesty, SFX makes programming intuitive and powerful.
What is SFX?
SFX is a programming language that fixes 50 years of accumulated lies in programming:
| The Lie | The Truth (SFX) |
|---|---|
0.1 + 0.2 ≠ 0.3 | 0.1 + 0.2 = 0.3 (arbitrary precision) |
| List[0] is first | List[1] is first (1-based indexing) |
| Null pointer errors | No null (safe defaults: 0, “”, False, []) |
| “👨👩👧👦”.Length = 7 | “👨👩👧👦”.Length = 1 (grapheme clustering) |
| Objects are static | Context-oriented (Situations modify behavior) |
| Manual cache updates | Reactive observers (self-healing data) |
Your First SFX Program
Story:
Print "Hello, SFX!"
# Mathematical honesty - no float errors!
Result is 0.1 + 0.2
Print Result # 0.3 ✓
# 1-based indexing - List[1] is first
Numbers is [10, 20, 30]
First is Numbers[1] # 10 ✓
Print "First number: " + First
Key Features at a Glance
- JIT Compilation: Automatic 2-5x performance boost with Cranelift
- Reactive Observers: Self-healing data that maintains consistency automatically
- Context-Oriented: Objects behave differently in different Situations
- Mathematical Honesty: Arbitrary precision arithmetic by default
- 1-Based Indexing: Lists start at 1, not 0
- No Null Pointers: Safe defaults prevent null reference errors
- Grapheme Clustering: Emoji counted correctly as single characters
- Powerful Standard Library: Data parsing, networking, concurrency, LLM integration
Current Version
Version 0.3.2 - All core features complete including:
- Core language with interpreter
- Full standard library (21 modules)
- JIT compilation with trace optimization
- Context system (Situations)
- Reactive When observers
- Concurrency (Tasks, Channels)
- LLM integration
What’s Next?
Ready to get started? Check out:
- Installation - Set up SFX on your system
- Quick Start - Learn the basics in 5 minutes
- Your First Program - Write your first SFX application
- Why SFX? - Understand the philosophy behind SFX
License
SFX is open source under the Apache License 2.0.
“Programming should be as natural as telling a story” - SFX
Installation
This guide will help you install SFX on your system.
Prerequisites
- Rust 1.75+ - SFX is written in Rust and requires Cargo to build
- Git - For cloning the repository
Installing Rust
If you don’t have Rust installed, get it from rustup.rs:
# Unix/Linux/macOS
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Windows
# Download and run rustup-init.exe from https://rustup.rs/
Installing SFX
From Source (Current Method)
# Clone the repository
git clone https://github.com/roriau0422/sfex-lang.git
cd sfex-lang
# Build in release mode
cargo build --release
# The sfex binary will be at target/release/sfex
Add to PATH (Optional)
To run sfex from anywhere:
Unix/Linux/macOS:
# Add to ~/.bashrc or ~/.zshrc
export PATH="$PATH:/path/to/sfex-lang/target/release"
Windows:
# Add to system PATH:
# Settings → System → About → Advanced system settings → Environment Variables
# Add: C:\path\to\sfex-lang\target\release
Verify Installation
Test that SFX is working:
./target/release/sfex --version
# Should print: sfex 0.3.2
# Run a test script
./target/release/sfex run tests/core/hello.sfex
Next Steps
- Quick Start - Learn the basics
- Your First Program - Write your first SFX script
Quick Start
Get up and running with SFX in 5 minutes!
Hello World
Create a file called hello.sfex:
Story:
Print "Hello, SFX!"
Run it:
sfex run hello.sfex
# Output: Hello, SFX!
Mathematical Honesty
SFX uses arbitrary precision arithmetic - no more float errors:
Story:
Result is 0.1 + 0.2
Print Result # 0.3 ✓ (not 0.30000000000000004)
1-Based Indexing
Lists start at 1, not 0:
Story:
Numbers is [10, 20, 30]
First is Numbers[1] # 10 (first element)
Second is Numbers[2] # 20
Third is Numbers[3] # 30
Print "First: " + First
Variables and Assignment
Use is for assignment:
Story:
Name is "Alice"
Age is 25
Score is 95.5
IsActive is True
Print Name
Print "Age: " + Age
Control Flow
If/Else
Story:
Score is 85
If Score >= 90:
Print "A grade"
Else If Score >= 80:
Print "B grade"
Else:
Print "Keep trying"
Loops
Story:
# Repeat a fixed number of times
Repeat 3 times:
Print "Hello!"
# Loop over a list
Colors is ["red", "green", "blue"]
For each Color in Colors:
Print Color
Working with Lists
Story:
# Create a list
Items is [1, 2, 3, 4, 5]
# Access elements (1-based)
First is Items[1]
# Get length
Count is Items.Length
Print "Count: " + Count # 5
# Add elements
Items.Add(6)
Working with Maps
Story:
# Create a map
User is { name: "Bob", age: 30, city: "New York" }
# Access values
Name is User["name"]
Age is User["age"]
Print "Name: " + Name
Print "Age: " + Age
Creating Objects (Concepts)
Concept: Person
Name
Age
To Greet:
Print "Hello, I'm " + This.Name
To Birthday:
Set This.Age to This.Age + 1
Story:
Create Person Called Alice
Set Alice.Name to "Alice"
Set Alice.Age to 30
Alice.Greet # Hello, I'm Alice
Alice.Birthday
Print Alice.Age # 31
File Operations
Story:
# Write to a file
Content is "Hello from SFX!"
File.Write("output.txt", Content)
# Read from a file
Data is File.Read("output.txt")
Print Data
HTTP Requests
Story:
# Make an HTTP GET request
Response is HTTP.Get("https://api.github.com/users/octocat")
Print Response["Status"] # 200
Print Response["Body"] # JSON response
What’s Next?
Now that you know the basics, dive deeper:
- Your First Program - Build a complete application
- Core Concepts - Understand SFX’s philosophy
- Language Syntax - Complete syntax reference
- Examples - More code examples
Your First Program
Let’s build a complete To-Do list application to learn SFX fundamentals.
The To-Do List App
Create a file called todo.sfex:
# To-Do List Application
Concept: TodoItem
Title
IsDone
To Display:
If This.IsDone:
Print "✓ " + This.Title
Else:
Print "☐ " + This.Title
Concept: TodoList
Items
To Initialize:
Set This.Items to []
To AddTask with Title:
Create TodoItem Called NewItem
Set NewItem.Title to Title
Set NewItem.IsDone to False
Set This.Items to This.Items + [NewItem]
Print "Added: " + Title
To ShowAll:
Print "\n=== My To-Do List ==="
If This.Items.Length = 0:
Print "No tasks yet!"
Else:
For each Item in This.Items:
Item.Display
To CompleteTask with Index:
If Index >= 1 and Index <= This.Items.Length:
Task is This.Items[Index]
Set Task.IsDone to True
Print "✓ Completed: " + Task.Title
# Update the item in the list
NewItems is []
Counter is 1
For each Item in This.Items:
If Counter = Index:
Set NewItems to NewItems + [Task]
Else:
Set NewItems to NewItems + [Item]
Set Counter to Counter + 1
Set This.Items to NewItems
Else:
Print "Invalid task number!"
Story:
# Create the to-do list
Create TodoList Called MyTodos
MyTodos.Initialize
# Add some tasks
MyTodos.AddTask with "Buy groceries"
MyTodos.AddTask with "Write documentation"
MyTodos.AddTask with "Learn SFX"
MyTodos.AddTask with "Build an app"
# Show all tasks
MyTodos.ShowAll
# Complete some tasks
Print "\n--- Completing tasks ---"
MyTodos.CompleteTask with 1
MyTodos.CompleteTask with 3
# Show updated list
MyTodos.ShowAll
Run the Program
sfex run todo.sfex
Expected output:
Added: Buy groceries
Added: Write documentation
Added: Learn SFX
Added: Build an app
=== My To-Do List ===
☐ Buy groceries
☐ Write documentation
☐ Learn SFX
☐ Build an app
--- Completing tasks ---
✓ Completed: Buy groceries
✓ Completed: Learn SFX
=== My To-Do List ===
✓ Buy groceries
☐ Write documentation
✓ Learn SFX
☐ Build an app
What Did We Learn?
1. Concepts (Classes)
Concept: TodoItem
Title # Field declaration
IsDone
To MarkDone: # Method definition
# Method body
Concepts are SFX’s version of classes. They contain:
- Fields - Data storage (Title, IsDone)
- Methods - Functions that operate on the object
2. Creating Instances
Create TodoList Called MyTodos
The Create statement creates a new instance of a Concept.
3. Setting Field Values
Set NewItem.Title to Title
Set NewItem.IsDone to False
The Set statement modifies object fields.
4. Method Calls
MyTodos.AddTask with "Buy groceries"
Item.Display
- Methods without parameters:
Item.Display - Methods with parameters:
MyTodos.AddTask with "Buy groceries"
5. This Keyword
To AddTask with Title:
Set This.Items to This.Items + [NewItem] # 'This' refers to current object
Inside methods, This refers to the current instance.
6. Lists
Set This.Items to [] # Create empty list
Set This.Items to This.Items + [NewItem] # Add element (concatenation)
Count is This.Items.Length # Get length
Task is This.Items[Index] # Access by index (1-based!)
Lists in SFX:
- Created with
[] - 1-based indexing - first element is at index 1
- Dynamic - grow by concatenating with
+ - Have
.Lengthproperty
7. Control Flow
If This.Items.Length = 0:
Print "No tasks yet!"
Else:
For each Item in This.Items:
Item.Display
If/Elsefor conditionalsFor eachfor iterating over lists
8. Parameters
To AddTask with Title:
# 'Title' is the parameter
Methods can take parameters using with ParameterName.
9. Value Semantics
To CompleteTask with Index:
Task is This.Items[Index] # Gets a copy of the item
Set Task.IsDone to True # Modifies the copy
# Need to update the original in the list
NewItems is []
Counter is 1
For each Item in This.Items:
If Counter = Index:
Set NewItems to NewItems + [Task] # Use modified copy
Else:
Set NewItems to NewItems + [Item]
Set Counter to Counter + 1
Set This.Items to NewItems
Important: When you get an item from a list, you get a copy, not a reference. To update an item in a list:
- Get the item (creates a copy)
- Modify the copy
- Rebuild the list with the modified copy
This “value semantics” approach ensures data safety - changes to copies don’t accidentally affect originals elsewhere.
Enhancing the Program
Add Task Priority
Concept: TodoItem
Title
IsDone
Priority # New field: 1=High, 2=Medium, 3=Low
To Display:
PriorityText is ""
When This.Priority:
is 1:
PriorityText is "[HIGH] "
is 2:
PriorityText is "[MEDIUM] "
is 3:
PriorityText is "[LOW] "
If This.IsDone:
Print "✓ " + PriorityText + This.Title
Else:
Print "☐ " + PriorityText + This.Title
Add Due Dates
Concept: TodoItem
Title
IsDone
DueDate
To IsOverdue:
Now is Time.Now()
If This.DueDate < Now:
Return True
Else:
Return False
What’s Next?
You’ve learned the fundamentals! Continue with:
- Core Concepts - Deep dive into SFX philosophy
- Language Syntax - Complete syntax reference
- Object-Oriented Programming - Advanced OOP features
- Reactive Programming - Self-healing data
- Context-Oriented Programming - Dynamic behavior changes
- Examples - More practical examples
Why SFX?
The Problem with Traditional Programming
For 50 years, programming languages have taught us lies that have become “normal”:
- “Don’t worry that 0.1 + 0.2 ≠ 0.3, you’ll get used to it”
- “Arrays start at 0 because… reasons”
- “Null pointer exceptions are just part of programming”
- “Emoji taking 7 characters is technically correct”
These aren’t features - they’re historical accidents that have become normalized.
The SFX Philosophy
SFX takes a different approach: Programming should match human intuition, not computer architecture.
“Programming the way humans think, not the way computers think”
1. Mathematical Honesty
The Problem:
// JavaScript, Python, Java, C++, etc.
0.1 + 0.2 // 0.30000000000000004 ❌
The SFX Solution:
Result is 0.1 + 0.2
Print Result # 0.3 ✓
SFX uses arbitrary precision arithmetic by default. Math works the way you learned in school.
2. Human-Friendly Indexing
The Problem:
# Most languages
items = [10, 20, 30]
first = items[0] # Why 0? Humans count from 1!
The SFX Solution:
Items is [10, 20, 30]
First is Items[1] # First item is 1, naturally!
3. No Null Pointers
The Problem:
// Java - the billion-dollar mistake
String name = null;
name.length(); // NullPointerException! 💥
The SFX Solution:
# All types have safe defaults:
# Numbers → 0
# Strings → ""
# Booleans → False
# Lists → []
# Maps → {}
# For optional values, use Option type:
Result is Some(42)
NoResult is None
If Result.IsSome:
Value is Result.Unwrap()
4. Grapheme Clustering
The Problem:
# Python, JavaScript, etc.
emoji = "👨👩👧👦"
len(emoji) # 7 ❌ (Why? It's ONE emoji!)
The SFX Solution:
Emoji is "👨👩👧👦"
Length is Emoji.Length # 1 ✓ (One emoji = one character)
5. Reactive Programming
The Problem:
// Manual updates everywhere
class Product {
constructor() {
this.price = 0;
this.tax = 0;
this.total = 0;
}
setPrice(price) {
this.price = price;
this.updateTax(); // Don't forget!
this.updateTotal(); // Don't forget!
}
updateTax() {
this.tax = this.price * 0.1;
this.updateTotal(); // Don't forget!
}
}
The SFX Solution:
Concept: Product
Price, Tax, Total
# Automatic - no manual updates needed!
When Price changes:
Set This.Tax to This.Price * 0.1
Set This.Total to This.Price + This.Tax
6. Context-Oriented Programming
The Problem:
// Traditional OOP - behavior is fixed
class User {
String getPermissions() {
return "read";
}
}
// To change behavior, you need inheritance, decorators,
// or manual if-checks everywhere
The SFX Solution:
Concept: User
To GetPermissions:
Return "read"
Situation: AdminMode
Adjust User:
To GetPermissions:
Return "admin,write,delete"
Story:
Create User Called Bob
Print Bob.GetPermissions # "read"
Switch on AdminMode
Print Bob.GetPermissions # "admin,write,delete"
Design Principles
1. Beginner-Friendly
SFX is designed for people learning to program:
- Natural syntax:
Name is "Alice"instead ofname = "Alice" - Clear keywords:
Repeat 10 timesinstead offor(i=0; i<10; i++) - No surprises: Math works correctly, lists start at 1, no null crashes
2. Powerful When Needed
Despite being beginner-friendly, SFX has advanced features:
- JIT Compilation: 2-5x automatic speedup
- Concurrency: Tasks and channels
- Networking: HTTP, WebSocket, TCP, UDP
- LLM Integration: Built-in OpenAI support
- Reactive Observers: Self-healing data
3. No Legacy Baggage
SFX was designed in 2024, not 1970. We don’t have to maintain backwards compatibility with mistakes from 50 years ago.
Who is SFX For?
Perfect For:
- Beginners learning to program
- Educators teaching programming concepts
- Rapid prototyping where correctness matters
- Business logic with financial calculations
- Scripts and automation that should “just work”
Maybe Not For:
- Systems programming (use Rust, C)
- Embedded systems with tight memory constraints
- High-frequency trading where microseconds matter (though JIT helps!)
- Large legacy codebases (migration would be significant)
The “AI Era” Design
SFX is designed for the AI era where:
- LLMs write a lot of code - SFX’s natural syntax is easier for AI to generate correctly
- Correctness matters - When AI generates financial calculations, 0.1 + 0.2 should equal 0.3
- Rapid iteration - JIT compilation means you get interpreter speed + compiler performance
Comparison
| Feature | SFX | Python | JavaScript | Java |
|---|---|---|---|---|
| Math correctness | ✓ | ✗ | ✗ | ✗ |
| 1-based indexing | ✓ | ✗ | ✗ | ✗ |
| No null crashes | ✓ | ✗ | ✗ | ✗ |
| Grapheme-aware | ✓ | ✗ | ✗ | ✗ |
| Reactive observers | ✓ | ✗ | ✗ | ✗ |
| Context-oriented | ✓ | ✗ | ✗ | ✗ |
| JIT compilation | ✓ | ✗ | ✓ | ✓ |
| Easy concurrency | ✓ | ~ | ~ | ~ |
Getting Started
Ready to try programming without the lies?
- Mathematical Honesty - Deep dive into correct math
- 1-Based Indexing - Why lists start at 1
- No Null Pointers - Safe defaults and Option types
- Grapheme Clustering - Unicode done right
Programming should be intuitive, not historical.
Mathematical Honesty
The Floating Point Lie
One of the most shocking things new programmers discover:
# Python (same in JavaScript, Java, C++, C#, Ruby, etc.)
result = 0.1 + 0.2
print(result) # 0.30000000000000004 ❌
Teachers say “don’t worry about it” or “you’ll get used to it.” But why should we?
Why Does This Happen?
Most languages use IEEE 754 floating-point arithmetic, which represents numbers in binary:
- 0.1 in binary is
0.0001100110011001100...(infinite repeating) - 0.2 in binary is
0.001100110011001100...(infinite repeating) - Binary can’t represent these exactly, so they’re rounded
- Adding rounded numbers gives wrong answers
This was a reasonable choice in 1970 when:
- Memory was expensive
- CPUs had hardware floating-point units
- Alternative precision libraries were slow
But it’s 2025 now. We can do better.
The SFX Solution
SFX uses arbitrary precision arithmetic by default:
Story:
Result is 0.1 + 0.2
Print Result # 0.3 ✓
# Financial calculations
Price is 99.99
Tax is Price * 0.08
Total is Price + Tax
Print Total # 107.9892 (exact!)
# Very large numbers
Big is 123456789123456789123456789
Bigger is Big * 2
Print Bigger # 246913578246913578246913578
How It Works
SFX uses the BigDecimal type for the default Number type:
- Numbers are stored as integers with a scale factor
- 0.1 is stored as
1with scale-1(meaning 1 × 10^-1) - Operations maintain exact precision
- No rounding errors accumulate
Example
Story:
# Exact decimal representation
A is 0.1 # Stored exactly
B is 0.2 # Stored exactly
C is A + B # 0.3 exactly
# This always works:
If C = 0.3:
Print "Math works!" # Always prints ✓
When You Need Speed: FastNumber
For performance-critical code (games, physics, graphics), use FastNumber:
Story:
# IEEE 754 float for speed
Speed is FastNumber(299792458.0)
Time is FastNumber(2.5)
Distance is Speed * Time
# About 10x faster than Number
# But you get float errors again:
Test is FastNumber(0.1) + FastNumber(0.2)
# 0.30000000000000004
When to use FastNumber:
- Physics simulations
- Graphics/game engines
- High-frequency calculations
- When performance > precision
When to use Number (default):
- Financial calculations
- User-facing math
- Counting/indexing
- When correctness matters
- Learning/teaching programming
Real-World Impact
Financial Software
Concept: Invoice
Subtotal, TaxRate, Tax, Total
When Subtotal changes:
Set This.Tax to This.Subtotal * This.TaxRate
Set This.Total to This.Subtotal + This.Tax
Story:
Create Invoice Called Bill
Set Bill.Subtotal to 99.99
Set Bill.TaxRate to 0.08
# Exact: $107.9892
# Float: $107.98919999999999 (wrong!)
Print Bill.Total
With floats, multiply this error by millions of transactions, and you’re off by thousands of dollars.
Scientific Computing
Story:
# Sum a series: 1 + 1/2 + 1/3 + 1/4 + ...
Sum is 0
Repeat 10000 times with I:
Sum is Sum + (1 / I)
# SFX: Exact answer
# Float: Accumulated rounding errors
Print Sum
Teaching Math
When teaching programming to kids, having 0.1 + 0.2 = 0.3 work correctly means:
- Less confusion
- Math matches what they learned in school
- One less “computers are weird” moment
- Focus on logic, not float quirks
Performance Comparison
| Operation | Number (exact) | FastNumber (float) |
|---|---|---|
| Addition | 100% | ~1000% (10x faster) |
| Multiplication | 100% | ~1000% |
| Division | 100% | ~1000% |
With JIT compilation, Number operations get optimized, narrowing the gap.
Examples
Banking
Concept: BankAccount
Balance
To Deposit with Amount:
Set This.Balance to This.Balance + Amount
To Withdraw with Amount:
Set This.Balance to This.Balance - Amount
Story:
Create BankAccount Called Checking
Set Checking.Balance to 0
# Deposit cents
Checking.Deposit with 0.01
Repeat 100 times:
Checking.Deposit with 0.01
# Balance is exactly $1.01
Print Checking.Balance # 1.01 ✓
With floats, you’d get 1.0100000000000007 or similar.
E-commerce
Concept: ShoppingCart
Items, Subtotal, Discount, Tax, Total
To CalculateTotal:
ItemsSum is 0
For each Item in This.Items:
ItemsSum is ItemsSum + Item["price"]
Set This.Subtotal to ItemsSum
DiscountAmount is This.Subtotal * This.Discount
TaxableAmount is This.Subtotal - DiscountAmount
TaxAmount is TaxableAmount * This.Tax
Set This.Total to TaxableAmount + TaxAmount
Story:
Create ShoppingCart Called Cart
Set Cart.Items to [
{ name: "Item 1", price: 19.99 },
{ name: "Item 2", price: 29.99 },
{ name: "Item 3", price: 9.99 }
]
Set Cart.Discount to 0.10 # 10% off
Set Cart.Tax to 0.08 # 8% tax
Cart.CalculateTotal
Print "Total: $" + Cart.Total # Exact to the cent!
Best Practices
1. Use Number by Default
# Good - exact
Price is 19.99
Tax is Price * 0.08
2. Use FastNumber for Performance
# Physics engine
Concept: Particle
X, Y, VX, VY
To Update with DT:
# Convert to FastNumber for speed
FDT is FastNumber(DT)
Set This.X to This.X + (This.VX * FDT)
Set This.Y to This.Y + (This.VY * FDT)
3. Don’t Mix Types Unnecessarily
# Avoid mixing Number and FastNumber
A is 10
B is FastNumber(20.0)
C is A + B # Works, but converts to FastNumber (loses precision)
4. Round for Display
# For user display, round to appropriate precision
Price is 19.99
Tax is Price * 0.085 # 1.69915
Total is Price + Tax # 21.68915
# Display as currency (2 decimal places)
DisplayTotal is Math.Round(Total * 100) / 100
Print "$" + DisplayTotal # $21.69
Summary
SFX’s mathematical honesty means:
✓ Correct by default - Math works like school taught you ✓ No surprises - 0.1 + 0.2 = 0.3 ✓ Financial accuracy - Pennies don’t go missing ✓ Easier to learn - No float quirks to explain ✓ FastNumber available - Performance when you need it
Programming languages should make correctness easy and performance opt-in, not the other way around.
Next: 1-Based Indexing - Why lists start at 1
1-Based Indexing
The Zero-Based Confusion
Quick question: What’s the first item in this list?
Shopping List:
1. Milk
2. Eggs
3. Bread
Everyone says “Milk” - it’s item #1.
Now, in most programming languages:
# Python, JavaScript, Java, C++, etc.
shopping = ["Milk", "Eggs", "Bread"]
first = shopping[0] # 0? But we just said it's #1!
Why this disconnect?
Historical Reasons
Zero-based indexing comes from C, where arrays are implemented as pointers:
// C language
int array[5];
// array[0] means: "address of array + (0 * sizeof(int))"
// array[3] means: "address of array + (3 * sizeof(int))"
This was a hardware optimization from the 1970s. The first element is offset by zero bytes, so accessing it is slightly faster.
But that’s an implementation detail! Why should humans care about memory offsets?
The SFX Solution
SFX uses 1-based indexing because that’s how humans count:
Story:
Shopping is ["Milk", "Eggs", "Bread"]
First is Shopping[1] # Milk ✓
Second is Shopping[2] # Eggs ✓
Third is Shopping[3] # Bread ✓
# The length matches the last index!
Count is Shopping.Length # 3
Last is Shopping[Count] # Bread ✓
Why 1-Based is Better
1. Matches Human Counting
Story:
# Task: Print the first 3 items
Items is ["A", "B", "C", "D", "E"]
Repeat 3 times with I:
Print Items[I] # I goes 1, 2, 3 - perfect!
Compare to zero-based:
# Confusing: Print items 0, 1, 2 to get the "first 3"
for i in range(3): # range(3) means 0,1,2 - huh?
print(items[i])
2. Length Matches Last Index
Story:
Numbers is [10, 20, 30, 40, 50]
Count is Numbers.Length # 5
Last is Numbers[Count] # 50 ✓ Makes sense!
Zero-based:
numbers = [10, 20, 30, 40, 50]
count = len(numbers) # 5
last = numbers[count] # ERROR! Out of bounds!
last = numbers[count - 1] # 50 (why -1?)
3. Fewer Off-By-One Errors
The infamous “off-by-one error” is often caused by zero-based thinking:
# Bug-prone (0-based)
for i in range(len(items)): # 0 to length-1
# Easy to forget the -1 in range(start, end-1)
# Or:
for i in range(1, len(items)): # Accidentally skips first item!
SFX:
# Clear (1-based)
Repeat Items.Length times with I:
Print Items[I] # I goes 1, 2, 3... naturally
4. Clearer Slicing
Story:
Numbers is [10, 20, 30, 40, 50]
# Get items 2 through 4
Slice is Numbers.Slice(2, 4) # [20, 30, 40] ✓
# Start at 2, end at 4, includes both!
Compare to zero-based (Python):
numbers = [10, 20, 30, 40, 50]
slice = numbers[1:4] # [20, 30, 40]
# Start at 1 (second item), end BEFORE 4 - confusing!
Common Scenarios
Looping with Index
Story:
Names is ["Alice", "Bob", "Charlie"]
# Natural counting
Repeat Names.Length times with I:
Print "Person " + I + ": " + Names[I]
# Output:
# Person 1: Alice
# Person 2: Bob
# Person 3: Charlie
Finding an Index
Story:
Items is ["apple", "banana", "cherry"]
Target is "banana"
Found is 0
Repeat Items.Length times with I:
If Items[I] = Target:
Found is I
Break
If Found > 0:
Print "Found at position " + Found # Position 2 ✓
Accessing First and Last
Story:
Numbers is [5, 10, 15, 20, 25]
First is Numbers[1] # 5
Last is Numbers[Numbers.Length] # 25
# Middle element (for odd-length lists)
Middle is Numbers[(Numbers.Length + 1) / 2]
Frequently Asked Questions
“But zero-based is more efficient!”
No. Modern compilers optimize both equally. The difference is nanoseconds.
SFX’s JIT compiler generates the same machine code whether you write array[1] or array[0] - it just adjusts the offset.
“Zero-based is standard everywhere!”
So were punch cards, GOTO statements, and null pointers. Just because something is common doesn’t make it right.
Many successful languages use 1-based indexing:
- MATLAB (engineering/scientific computing)
- R (statistics)
- Lua (game scripting)
- Fortran (still used in supercomputing)
“What about C interop?”
SFX’s stdlib handles the conversion. When you call a C library, SFX adjusts indices automatically.
# You write (1-based)
Result is SomeArray[1]
# SFX internally (0-based for C interop)
# result = some_array[0]
“I’ll get confused switching between languages!”
People switch between:
- Languages that start weeks on Sunday vs Monday
- Countries that drive on left vs right
- Date formats (MM/DD/YY vs DD/MM/YY)
Programmers are smart. You can handle lists starting at 1 in SFX and 0 in Python.
Examples
Ranking
Concept: Leaderboard
Scores
To ShowRankings:
# Sort in descending order
Sorted is This.Scores.Sort("desc")
Print "=== Rankings ==="
Repeat Sorted.Length times with Position:
Player is Sorted[Position]
Print "#" + Position + ": " + Player["name"] + " - " + Player["score"]
Story:
Create Leaderboard Called Board
Set Board.Scores to [
{ name: "Alice", score: 95 },
{ name: "Bob", score: 87 },
{ name: "Charlie", score: 92 }
]
Board.ShowRankings
# Output:
# === Rankings ===
# #1: Alice - 95
# #2: Charlie - 92
# #3: Bob - 87
Notice how the rank matches the index naturally!
Pagination
Story:
AllItems is [/* 100 items */]
PageSize is 10
CurrentPage is 1
# Page 1: items 1-10
# Page 2: items 11-20
# etc.
StartIndex is ((CurrentPage - 1) * PageSize) + 1
EndIndex is CurrentPage * PageSize
# Get items for current page
PageItems is AllItems.Slice(StartIndex, EndIndex)
Grid/Matrix
Story:
# 3x3 grid
Grid is [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# Access row 2, column 3
Value is Grid[2][3] # 6 (second row, third item)
# Human-readable coordinates
Print "Row 1, Col 1: " + Grid[1][1] # 1 ✓
Migration from 0-Based Languages
If you’re coming from Python/JavaScript/Java:
# Python (0-based)
first = items[0]
second = items[1]
last = items[len(items) - 1]
# SFX (1-based)
First is Items[1]
Second is Items[2]
Last is Items[Items.Length]
Tip: Think “first, second, third” not “zeroth, first, second.”
Best Practices
Loop from 1 to Length
# Good
Repeat List.Length times with I:
Print List[I]
Use For Each When You Don’t Need Index
# Even better when index isn't needed
For each Item in List:
Print Item
Check Bounds
# Safe access
Index is 5
If Index >= 1 and Index <= List.Length:
Value is List[Index]
Else:
Print "Index out of range"
Summary
SFX uses 1-based indexing because:
✓ Natural - Matches how humans count ✓ Clearer - First item is index 1, not 0 ✓ Consistent - Length equals last index ✓ Fewer errors - Less off-by-one confusion ✓ No performance cost - JIT optimizes equally
Programming is for humans, not computers. Let’s count like humans.
Next: No Null Pointers - Safe defaults and the Option type
No Null Pointers
The Billion-Dollar Mistake
In 2009, Tony Hoare, inventor of null references, called it his “billion-dollar mistake”:
“I call it my billion-dollar mistake… null references have led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage.”
// Java - the typical null nightmare
String name = null;
int length = name.length(); // NullPointerException! 💥
// JavaScript
let user = null;
console.log(user.email); // TypeError: Cannot read property 'email' of null 💥
# Python
person = None
age = person.age # AttributeError: 'NoneType' object has no attribute 'age' 💥
These crashes happen because:
- Variables can be null
- You forget to check for null
- Program crashes at runtime
The SFX Solution
SFX eliminates null pointers with two approaches:
1. Safe Defaults
Every type has a sensible default value:
Concept: Person
Name # Defaults to "" (empty string)
Age # Defaults to 0
Active # Defaults to False
Tags # Defaults to [] (empty list)
Meta # Defaults to {} (empty map)
Story:
Create Person Called Bob
# No need to initialize - safe defaults!
Print Bob.Name # "" (empty, not null)
Print Bob.Age # 0
Print Bob.Active # False
Print Bob.Tags # []
# Safe to use immediately
Length is Bob.Name.Length # 0 (works! no crash!)
2. Option Type
For values that might be absent, use Option:
# Some(value) or None
Result is Some(42)
NoResult is None
# Check if present
If Result.IsSome:
Print "Has value!"
If NoResult.IsNone:
Print "No value!"
# Extract value safely
If Result.IsSome:
Value is Result.Unwrap()
Print Value # 42
# Provide fallback
Default is NoResult.UnwrapOr(0)
Print Default # 0 (because NoResult is None)
Why This is Better
No Null Pointer Crashes
# SFX - always safe
Concept: User
Email
Story:
Create User Called Guest
# Email is "" (empty string), not null
Length is Guest.Email.Length # 0 ✓ (no crash!)
If Guest.Email = "":
Print "No email provided"
Compare to:
// Java - potential crash
User guest = new User();
int length = guest.email.length(); // NullPointerException if email is null!
Explicit Optional Values
Concept: SearchResult
Result # Option type
To Find with Query:
# Simulate search
If Query = "found":
Set This.Result to Some("Item found!")
Else:
Set This.Result to None
Story:
Create SearchResult Called Search
Search.Find with "found"
If Search.Result.IsSome:
Print Search.Result.Unwrap() # "Item found!"
Search.Find with "missing"
If Search.Result.IsNone:
Print "Nothing found" # This prints
Safe Method Chaining
Concept: Config
Database # Map with database config
Story:
Create Config Called Settings
Set Settings.Database to {} # Empty map (not null!)
# Safe to access nested properties
Host is Settings.Database["host"] # Returns "" if not set
Port is Settings.Database["port"] # Returns 0 if not set
# No crashes!
Default Values by Type
| Type | Default Value | Example |
|---|---|---|
| Number | 0 | Age defaults to 0 |
| FastNumber | 0.0 | Speed defaults to 0.0 |
| String | “” | Name defaults to "" |
| Boolean | False | Active defaults to False |
| List | [] | Items defaults to [] |
| Map | {} | Config defaults to {} |
| Option | None | Result defaults to None |
Option Type API
Creating Options
Story:
HasValue is Some(42)
NoValue is None
# From expressions
Result is If Score > 50 then Some(Score) else None
Checking for Values
Story:
Result is Some(100)
# Check if has value
If Result.IsSome:
Print "Has value"
If Result.IsNone:
Print "No value"
Extracting Values
Story:
Result is Some(42)
# Unwrap (crashes if None!)
Value is Result.Unwrap()
Print Value # 42
# Unwrap with default (safe)
Value is Result.UnwrapOr(0)
Print Value # 42 (or 0 if None)
NoResult is None
Value is NoResult.UnwrapOr(99)
Print Value # 99 (fallback used)
Real-World Examples
Database Query
Concept: Database
To FindUser with Id:
# Returns Option - user might not exist
If Id = 1:
Return Some({ name: "Alice", age: 30 })
Else:
Return None
Story:
Create Database Called DB
Result is DB.FindUser with 1
If Result.IsSome:
User is Result.Unwrap()
Print "Found: " + User["name"]
Else:
Print "User not found"
Result is DB.FindUser with 999
If Result.IsNone:
Print "User not found" # This prints
Configuration
Concept: AppConfig
Settings # Map
To GetSetting with Key:
# Return Option - setting might not exist
If This.Settings.HasKey(Key):
Return Some(This.Settings[Key])
Else:
Return None
To GetSettingWithDefault with Key and Default:
Result is This.GetSetting with Key
Return Result.UnwrapOr(Default)
Story:
Create AppConfig Called Config
Set Config.Settings to { timeout: 30, retries: 3 }
# Get with Option
Timeout is Config.GetSetting with "timeout"
If Timeout.IsSome:
Print "Timeout: " + Timeout.Unwrap()
# Get with default
MaxConnections is Config.GetSettingWithDefault with "max_connections" and 100
Print "Max connections: " + MaxConnections # 100 (default used)
Parsing
Concept: Parser
To ParseInt with Text:
# Try to parse, return Option
Try:
Number is Int.Parse(Text)
Return Some(Number)
Catch Error:
Return None
Story:
Create Parser Called P
Result is P.ParseInt with "42"
If Result.IsSome:
Num is Result.Unwrap()
Print "Parsed: " + Num # 42
Result is P.ParseInt with "not a number"
If Result.IsNone:
Print "Parse failed" # This prints
Caching
Concept: Cache
Data # Map
To Get with Key:
If This.Data.HasKey(Key):
Return Some(This.Data[Key])
Else:
Return None
To GetOrCompute with Key and ComputeFunc:
Cached is This.Get with Key
If Cached.IsSome:
Return Cached.Unwrap()
Else:
Value is ComputeFunc()
This.Data[Key] is Value
Return Value
Story:
Create Cache Called MyCache
Set MyCache.Data to {}
# Cache miss - compute
Value is MyCache.GetOrCompute with "expensive" and ExpensiveCalculation
Print Value
# Cache hit - return cached
Value is MyCache.GetOrCompute with "expensive" and ExpensiveCalculation
Print Value # Same value, no recomputation
Best Practices
1. Use Safe Defaults for Always-Present Data
Concept: Person
Name # Always has a name (even if "")
Age # Always has an age (even if 0)
Email # Always has an email (even if "")
# Safe defaults, no null checks needed
2. Use Option for Maybe-Present Data
Concept: User
Username
ProfilePicture # Option - might not have one
To GetProfilePicture:
# Return Option
If This.ProfilePicture.IsSome:
Return This.ProfilePicture
Else:
Return None
3. Provide Fallbacks with UnwrapOr
Story:
Config is LoadConfig()
Timeout is Config.GetSetting with "timeout"
# Use fallback if not set
ActualTimeout is Timeout.UnwrapOr(30)
4. Check Before Unwrap
# Good - safe
If Result.IsSome:
Value is Result.Unwrap()
# Risky - crashes if None
Value is Result.Unwrap()
5. Use Option for Return Values That Can Fail
Concept: Finder
To Find with Query:
# Might not find anything
If Found:
Return Some(Result)
Else:
Return None # Explicit: no result
Comparison with Other Languages
| Language | Null Handling |
|---|---|
| Java | null everywhere, can crash anytime |
| JavaScript | null and undefined, both cause crashes |
| Python | None, causes crashes |
| Rust | Option<T>, must handle (like SFX) |
| Kotlin | Nullable types T?, must check |
| SFX | Safe defaults + Option type ✓ |
SFX combines the best of both worlds:
- Safe defaults for simplicity (beginners)
- Option type for explicit optionality (advanced)
Summary
SFX eliminates null pointer crashes through:
✓ Safe defaults - Every type has a sensible default (0, “”, False, [], {}) ✓ Option type - Explicit handling of maybe-present values ✓ No null crashes - Can’t access null, because there is no null ✓ Clearer code - Intent is explicit (Some vs None) ✓ Beginner-friendly - Safe by default, no crashes
The result? Fewer bugs, clearer intent, safer code.
Next: Grapheme Clustering - Unicode done right
Grapheme Clustering
The Emoji Problem
Quick question: How many characters is this emoji?
👨👩👧👦
Most programming languages say:
# Python
emoji = "👨👩👧👦"
len(emoji) # 7 ❌
// JavaScript
let emoji = "👨👩👧👦";
emoji.length; # 11 ❌
// Java
String emoji = "👨👩👧👦";
emoji.length(); // 11 ❌
But it’s ONE emoji! The “family” emoji is a single visual unit.
SFX gets it right:
Story:
Family is "👨👩👧👦"
Length is Family.Length # 1 ✓
What Are Grapheme Clusters?
A grapheme cluster is what humans perceive as a single character:
a- Simple character (1 grapheme)é- Can be one character ORe+ combining accent (still 1 grapheme)👨👩👧👦- Multiple Unicode code points (but 1 grapheme)🇺🇸- Two code points (but 1 flag = 1 grapheme)
Technical Details (Optional)
Unicode has different representations:
- Code points - Individual Unicode values (U+0041, U+1F600, etc.)
- Code units - UTF-8 bytes, UTF-16 words
- Grapheme clusters - What humans see as characters ✓
Most languages count code points or code units. SFX counts grapheme clusters.
Why This Matters
1. String Length
Story:
# Simple ASCII
Name is "Alice"
Print Name.Length # 5 ✓
# Emoji
Emoji is "🎉"
Print Emoji.Length # 1 ✓
# Complex emoji
Family is "👨👩👧👦"
Print Family.Length # 1 ✓
# Flag emoji
Flag is "🇺🇸"
Print Flag.Length # 1 ✓
# Combining diacritics
Accented is "é" # Can be one code point or e + ́
Print Accented.Length # 1 ✓ (regardless of representation)
Compare to other languages:
# Python counts code points
"🎉".len() # 1 (lucky - simple emoji)
"👨👩👧👦".len() # 7 ❌ (complex emoji)
"🇺🇸".len() # 2 ❌ (flag is 2 code points)
2. String Slicing
Story:
Text is "Hello 👋 World 🌍"
# Get first 7 graphemes
Slice is Text.Slice(1, 7)
Print Slice # "Hello 👋" ✓ (emoji counts as 1)
Other languages:
# Python
text = "Hello 👋 World 🌍"
slice = text[0:7] # Might cut emoji in half! 💥
3. Text Truncation
Concept: TextTruncator
To Truncate with Text and MaxLength:
If Text.Length <= MaxLength:
Return Text
Else:
Truncated is Text.Slice(1, MaxLength - 3)
Return Truncated + "..."
Story:
Create TextTruncator Called T
Short is "Hello"
Print T.Truncate with Short and 10 # "Hello"
Long is "Hello 👋 World 🌍 Everyone 🎉"
Print T.Truncate with Long and 15 # "Hello 👋 World..." ✓
# Emoji not split!
4. Character Validation
Story:
# Validate username length
Username is "Alice🎮"
MinLength is 3
MaxLength is 20
If Username.Length >= MinLength and Username.Length <= MaxLength:
Print "Valid username" # This prints ✓
# "Alice🎮" is 6 graphemes (A-l-i-c-e-🎮)
5. Display Width
Concept: TextRenderer
To PadRight with Text and Width:
# Pad with spaces to reach width
CurrentLength is Text.Length
If CurrentLength >= Width:
Return Text
Else:
Padding is Width - CurrentLength
Spaces is ""
Repeat Padding times:
Spaces is Spaces + " "
Return Text + Spaces
Story:
Create TextRenderer Called Renderer
# All aligned, even with emoji
Print Renderer.PadRight with "Name" and 15 + "| Status"
Print Renderer.PadRight with "Alice" and 15 + "| Active"
Print Renderer.PadRight with "Bob 🎮" and 15 + "| Offline"
# Output:
# Name | Status
# Alice | Active
# Bob 🎮 | Offline
Common Emoji Patterns
Simple Emoji
Story:
# Single code point emoji
Smile is "😀" # 1 grapheme ✓
Heart is "❤️" # 1 grapheme ✓
Star is "⭐" # 1 grapheme ✓
Total is Smile.Length + Heart.Length + Star.Length
Print Total # 3 ✓
Skin Tone Modifiers
Story:
# Emoji + skin tone = 1 grapheme
WaveLight is "👋🏻" # 1 grapheme ✓
WaveDark is "👋🏿" # 1 grapheme ✓
Print WaveLight.Length # 1 ✓
Combined Emoji (ZWJ Sequences)
Story:
# Zero-Width Joiner combines emoji
Family is "👨👩👧👦" # 1 grapheme ✓
Couple is "👨❤️👨" # 1 grapheme ✓
FemaleFirefighter is "👩🚒" # 1 grapheme ✓
Total is Family.Length + Couple.Length + FemaleFirefighter.Length
Print Total # 3 ✓
Flag Emoji
Story:
# Two regional indicator symbols = 1 flag
US is "🇺🇸" # 1 grapheme ✓
UK is "🇬🇧" # 1 grapheme ✓
Japan is "🇯🇵" # 1 grapheme ✓
Flags is US + UK + Japan
Print Flags.Length # 3 ✓
Real-World Examples
Tweet Length Counter
Concept: TweetValidator
MaxLength is 280
To IsValid with Text:
Return Text.Length <= This.MaxLength
To GetRemainingChars with Text:
Return This.MaxLength - Text.Length
Story:
Create TweetValidator Called Validator
Tweet is "Hello world! 👋🌍🎉"
If Validator.IsValid with Tweet:
Remaining is Validator.GetRemainingChars with Tweet
Print "Valid! " + Remaining + " characters remaining"
# Counts emoji correctly!
Username Validation
Concept: UsernameValidator
MinLength is 3
MaxLength is 20
To Validate with Username:
Length is Username.Length
If Length < This.MinLength:
Return "Username too short (min " + This.MinLength + " characters)"
Else If Length > This.MaxLength:
Return "Username too long (max " + This.MaxLength + " characters)"
Else:
Return "Valid"
Story:
Create UsernameValidator Called Validator
# All valid - emoji count as 1 character each
Print Validator.Validate with "Alice" # Valid
Print Validator.Validate with "Bob🎮" # Valid
Print Validator.Validate with "游戏玩家" # Valid (Chinese characters)
Print Validator.Validate with "مستخدم" # Valid (Arabic)
Text Editor
Concept: TextEditor
Content
CursorPosition
To MoveCursorRight:
If This.CursorPosition < This.Content.Length:
Set This.CursorPosition to This.CursorPosition + 1
To MoveCursorLeft:
If This.CursorPosition > 0:
Set This.CursorPosition to This.CursorPosition - 1
To DeleteCharacter:
# Delete character at cursor
Before is This.Content.Slice(1, This.CursorPosition - 1)
After is This.Content.Slice(This.CursorPosition + 1, This.Content.Length)
Set This.Content to Before + After
# Deletes entire grapheme cluster, not just one byte!
Story:
Create TextEditor Called Editor
Set Editor.Content to "Hello 👨👩👧👦 World"
Set Editor.CursorPosition to 7
# Delete the family emoji
Editor.DeleteCharacter
Print Editor.Content # "Hello World" ✓
# Entire emoji deleted, not corrupted!
Performance
Grapheme clustering is slightly slower than byte counting:
Story:
# For ASCII-only strings, fast
Text is "Hello World"
Length is Text.Length # Fast
# For Unicode strings, slightly slower
Text is "Hello 👨👩👧👦 World 🌍"
Length is Text.Length # Slightly slower (but still fast!)
But correctness > speed. And with JIT compilation, the difference is minimal.
Best Practices
1. Use .Length for Character Count
# Good - grapheme count
Text is "Hello 👋"
Count is Text.Length # 7 (H-e-l-l-o-space-👋)
2. Use .ByteSize for Storage Size
# If you need byte count for storage/network
Text is "Hello 👋"
Bytes is Text.ByteSize # More than 7 (UTF-8 bytes)
3. Slice by Graphemes
# SFX slices by graphemes
Text is "Hello 👋 World"
First7 is Text.Slice(1, 7) # "Hello 👋" (emoji not split)
4. Validate Input by Grapheme Count
# Validate display length, not byte length
Username is "User🎮"
If Username.Length > 20:
Print "Username too long"
Comparison with Other Languages
| Language | “👨👩👧👦”.Length | Method |
|---|---|---|
| SFX | 1 ✓ | Grapheme clusters |
| Python | 7 | Code points |
| JavaScript | 11 | UTF-16 code units |
| Java | 11 | UTF-16 code units |
| Go | 25 | UTF-8 bytes |
| Rust | 25 | UTF-8 bytes (default) |
| Swift | 1 ✓ | Grapheme clusters |
SFX and Swift get it right by default!
Summary
SFX uses grapheme clustering for strings:
✓ Human-centric - Counts what humans see as characters ✓ Emoji-friendly - 👨👩👧👦 is 1 character, not 7 ✓ International - Works with all languages (Chinese, Arabic, emoji, etc.) ✓ Safe slicing - Won’t split multi-byte characters ✓ Correct validation - Username/tweet length validation works correctly
When dealing with text in the 21st century, grapheme clustering is essential.
Next: Basic Syntax - Learn SFX syntax fundamentals
Basic Syntax
SFX uses a clean, readable syntax inspired by natural language. This chapter covers the fundamental syntax rules.
Program Structure
Every SFX program has a Story block - the main entry point:
Story:
Print "Hello, SFX!"
A complete program can include:
- Concepts - Classes/objects (defined before Story)
- Situations - Context modifiers (defined before Story)
- Story - Main entry point (required)
# Complete program structure
Concept: MyClass
Field1, Field2
Situation: MyContext
Adjust MyClass:
# Adjustments here
Story:
# Main program code here
Print "Starting program..."
Indentation
SFX uses significant indentation like Python:
- Use 4 spaces per indentation level
- DO NOT use tabs (use spaces only)
- Consistent indentation defines code blocks
Story:
If True:
Print "This is indented"
Print "So is this"
Print "Back to outer level"
Comments
Use # for single-line comments:
# This is a comment
Story:
Print "Hello" # Inline comment
# Another comment
Note: SFX currently supports only single-line comments. Multi-line comments are not yet supported.
Statements
Statements in SFX typically end at the end of the line:
Story:
Name is "Alice"
Age is 25
Print Name
Keywords
SFX uses descriptive English keywords:
| Category | Keywords |
|---|---|
| Structure | Story, Concept, Situation |
| Assignment | is, Set, to |
| Control Flow | If, Else, Repeat, For, each, in, When, Otherwise |
| Object Creation | Create, Called |
| Methods | To, with, and, Return |
| Context | Switch, on, off, Adjust, Proceed |
| Error Handling | Try, Catch, Always |
| Concurrency | Do, in, background |
| Logic | True, False, and, or, not |
| Loops | Break, Continue, times, while |
Case Sensitivity
SFX keywords are case-sensitive:
Story:
# Correct
Print "Hello"
# Wrong (will cause error)
print "Hello" # lowercase 'p'
PRINT "Hello" # uppercase
Variable and concept names are also case-sensitive:
Story:
Name is "Alice"
name is "Bob"
# 'Name' and 'name' are different variables
Line Continuation
Long statements can be continued on the next line within certain contexts:
# Lists and maps can span multiple lines
Items is [
"Item 1",
"Item 2",
"Item 3"
]
User is {
name: "Alice",
age: 30,
email: "alice@example.com"
}
Naming Conventions
Variables and Fields
Use PascalCase for variable and field names:
Story:
FirstName is "Alice"
LastName is "Smith"
UserAge is 25
IsActive is True
Concepts
Use PascalCase for concept names:
Concept: Person
Concept: ShoppingCart
Concept: HttpClient
Methods
Use PascalCase for method names:
Concept: Calculator
To Add with X and Y:
Return X + Y
To CalculateTotal:
# Method code
Situations
Use PascalCase for situation names:
Situation: AdminMode
Situation: DebugMode
Situation: VIPCustomer
Best Practices
1. Use Descriptive Names
# Good
UserName is "Alice"
ShoppingCart is []
TotalPrice is 99.99
# Less clear
UN is "Alice"
SC is []
TP is 99.99
2. Keep Indentation Consistent
# Good
Story:
If Condition:
DoSomething()
DoSomethingElse()
# Bad (inconsistent indentation)
Story:
If Condition:
DoSomething()
DoSomethingElse() # Error!
3. One Statement Per Line
# Good
Name is "Alice"
Age is 25
# Possible but discouraged
Name is "Alice", Age is 25 # Works only with Set statements
4. Group Related Code
Story:
# Configuration
Host is "localhost"
Port is 8080
# User setup
Create User Called Admin
Set Admin.Name to "Administrator"
Set Admin.Role to "admin"
# Start server
Server.Start with Host and Port
5. Use Comments for Clarity
Story:
# Calculate tax (10% rate)
Price is 100
Tax is Price * 0.1
Total is Price + Tax
# Display result
Print "Total with tax: $" + Total
Example: Complete Program
# User Management System
Concept: User
Name, Email, Role, IsActive
To Activate:
Set This.IsActive to True
Print This.Name + " has been activated"
To Deactivate:
Set This.IsActive to False
Print This.Name + " has been deactivated"
To UpdateEmail with NewEmail:
Set This.Email to NewEmail
Print "Email updated to: " + NewEmail
Situation: AdminMode
Adjust User:
To UpdateEmail with NewEmail:
# Admin can force update
Set This.Email to NewEmail
Print "[ADMIN] Email forcefully updated to: " + NewEmail
Story:
# Create users
Create User Called Alice
Set Alice.Name to "Alice Smith"
Set Alice.Email to "alice@example.com"
Set Alice.Role to "user"
Set Alice.IsActive to True
Create User Called Bob
Set Bob.Name to "Bob Jones"
Set Bob.Email to "bob@example.com"
Set Bob.Role to "admin"
Set Bob.IsActive to False
# Activate Bob
Bob.Activate
# Update Alice's email
Alice.UpdateEmail with "alice.smith@example.com"
# Admin mode - force update
Switch on AdminMode
Bob.UpdateEmail with "admin@example.com"
Switch off AdminMode
Print "=== User Summary ==="
Print Alice.Name + " - " + Alice.Email + " (Active: " + Alice.IsActive + ")"
Print Bob.Name + " - " + Bob.Email + " (Active: " + Bob.IsActive + ")"
What’s Next?
Now that you understand the basics, learn about:
- Variables and Assignment - How to work with variables
- Data Types - Numbers, strings, lists, maps, and more
- Operators - Arithmetic, comparison, logical operators
- Comments - Documenting your code
- Control Flow - Making decisions and loops
Variables and Assignment
Variables in SFX store data that can be used and modified throughout your program.
Assignment with is
Use the is keyword to assign values to variables:
Story:
Name is "Alice"
Age is 25
Score is 95.5
IsActive is True
Naming Rules
Variable names in SFX:
- Use PascalCase - Start with uppercase letter
- Can contain letters, numbers, and underscores
- Case-sensitive -
Nameandnameare different variables - Cannot start with a number
- Cannot be a reserved keyword
Valid Names
Story:
UserName is "Alice"
User2 is "Bob"
FirstName is "Charlie"
Total_Score is 100
IsActive is True
Age25 is 25
Invalid Names
Story:
# Error - starts with number
2User is "Invalid"
# Error - reserved keyword
If is "Invalid"
# Error - starts with lowercase (convention, not enforced)
userName is "Alice" # Works but violates convention
Variable Scope
Variables have different scopes depending on where they’re declared:
Story-Level Variables (Global)
Story:
GlobalVar is 100 # Visible throughout Story
If True:
Print GlobalVar # Can access here
LocalVar is 50
# LocalVar not accessible here
Method-Level Variables (Local)
Concept: Calculator
To Calculate:
Result is 10 + 20 # Local to this method
Return Result
Story:
Create Calculator Called Calc
Answer is Calc.Calculate
Print Answer # 30
# Cannot access 'Result' here - it's local to Calculate method
Block-Level Variables
Story:
If True:
BlockVar is "Inside If"
Print BlockVar # Works
# BlockVar not accessible here
Repeat 3 times:
LoopVar is "Inside Loop"
Print LoopVar # Works
# LoopVar not accessible here
Reassignment
Variables can be reassigned with the is keyword:
Story:
Counter is 0
Print Counter # 0
Counter is 10
Print Counter # 10
Counter is Counter + 5
Print Counter # 15
Multiple Assignments
You can assign multiple variables in sequence:
Story:
X is 10
Y is 20
Z is 30
# All separate statements
For object fields, you can use comma-separated Set statements:
Concept: Point
X, Y, Z
Story:
Create Point Called P
Set P.X to 1, Set P.Y to 2, Set P.Z to 3
Type Inference
SFX automatically determines the type of a variable from its value:
Story:
# Number (BigDecimal)
Age is 25
# String
Name is "Alice"
# Boolean
Active is True
# List
Items is [1, 2, 3]
# Map
User is { name: "Bob", age: 30 }
# FastNumber (explicit)
Speed is FastNumber(299792458.0)
Default Values
When fields are declared in Concepts without initialization, they get safe defaults:
Concept: Person
Name # Defaults to ""
Age # Defaults to 0
Active # Defaults to False
Tags # Defaults to []
Meta # Defaults to {}
Story:
Create Person Called User
Print User.Name # ""
Print User.Age # 0
Print User.Active # False
Constants
SFX doesn’t have a special const keyword, but by convention, use all-caps names for values that shouldn’t change:
Story:
MAX_USERS is 100
API_KEY is "secret"
DEFAULT_TIMEOUT is 30
# Convention: don't reassign these
Variable Shadowing
Variables in inner scopes can shadow outer variables:
Story:
X is 10
Print X # 10
If True:
X is 20 # Shadows outer X
Print X # 20
Print X # Still 10 (outer X unchanged)
Working with Different Types
Numbers
Story:
# Integer-like
Count is 42
# Decimal
Price is 19.99
# Negative
Debt is -50
# Large numbers (arbitrary precision)
BigNum is 123456789123456789123456789
Strings
Story:
# Simple string
Name is "Alice"
# With escape sequences
Message is "Line 1\nLine 2"
# Multiline (triple quotes)
Text is """This is
a multiline
string"""
# Concatenation
Greeting is "Hello, " + Name
Booleans
Story:
IsActive is True
IsDeleted is False
# From comparisons
IsAdult is Age >= 18
HasAccess is Role = "admin"
Lists
Story:
# Empty list
Items is []
# With values
Numbers is [1, 2, 3, 4, 5]
# Mixed types (not recommended)
Mixed is [1, "two", True]
# Multiline
Colors is [
"red",
"green",
"blue"
]
Maps
Story:
# Empty map
Config is {}
# With values
User is { name: "Alice", age: 25 }
# Multiline
Settings is {
host: "localhost",
port: 8080,
debug: True
}
Common Patterns
Swap Variables
Story:
A is 10
B is 20
# Swap using temporary variable
Temp is A
A is B
B is Temp
Print A # 20
Print B # 10
Accumulator Pattern
Story:
Sum is 0
Numbers is [1, 2, 3, 4, 5]
For each Num in Numbers:
Sum is Sum + Num
Print Sum # 15
Counter Pattern
Story:
Count is 0
Repeat 10 times:
Count is Count + 1
Print Count # 10
Conditional Assignment
Story:
Score is 85
Grade is ""
If Score >= 90:
Grade is "A"
Else If Score >= 80:
Grade is "B"
Else:
Grade is "C"
Print Grade # "B"
Best Practices
1. Use Descriptive Names
# Good
TotalPrice is 99.99
UserEmail is "alice@example.com"
MaxRetries is 3
# Bad
TP is 99.99
UE is "alice@example.com"
MR is 3
2. Initialize Before Use
# Good
Counter is 0
Counter is Counter + 1
# Bad
Counter is Counter + 1 # Error if Counter doesn't exist
3. Keep Scope Minimal
# Good - declare where needed
If Condition:
TempResult is Calculate()
Print TempResult
# Less good - unnecessarily wide scope
TempResult is 0
If Condition:
TempResult is Calculate()
Print TempResult
4. Use Meaningful Initial Values
# Good - clear intent
UserCount is 0
IsLoggedIn is False
ErrorMessage is ""
# Less clear
UserCount is -1 # Why -1?
IsLoggedIn is 0 # Use Boolean!
5. Don’t Reuse Variables for Different Purposes
# Bad
Temp is Calculate1()
Print Temp
Temp is Calculate2() # Confusing - different meaning
Print Temp
# Good
Result1 is Calculate1()
Print Result1
Result2 is Calculate2()
Print Result2
Examples
Configuration
Story:
# Application settings
AppName is "My App"
Version is "1.0.0"
Debug is True
# Database config
DbHost is "localhost"
DbPort is 5432
DbName is "myapp_db"
# API settings
ApiTimeout is 30
MaxRetries is 3
User Input Processing
Story:
# Simulated user input
UserInput is "42"
# Parse to number
InputNumber is 0 # Default
Try:
InputNumber is Int.Parse(UserInput)
Catch Error:
Print "Invalid input, using default"
Print "Value: " + InputNumber
Data Transformation
Story:
# Original data
FirstName is "alice"
LastName is "smith"
# Transform
FullName is FirstName.ToUpper + " " + LastName.ToUpper
Print FullName # "ALICE SMITH"
# Calculate derived values
Price is 100
TaxRate is 0.08
Tax is Price * TaxRate
Total is Price + Tax
Print "Total: $" + Total
Summary
- Use
isfor assignment:Name is "Alice" - Variables are case-sensitive:
Name≠name - PascalCase naming convention:
UserName,TotalScore - Type inference: SFX determines types automatically
- Block scope: Variables live within their declaration block
- Safe defaults: Concept fields default to 0, “”, False, [], {}
Next: Data Types - Learn about all SFX data types
Data Types
SFX has a rich type system designed to be both beginner-friendly and powerful. All types are automatically inferred from values.
Overview
| Type | Description | Example |
|---|---|---|
| Number | Arbitrary precision decimal | 42, 3.14 |
| FastNumber | IEEE 754 float (f64) | FastNumber(3.14) |
| String | Grapheme-aware text | "Hello", """Multi-line""" |
| Boolean | True or False | True, False |
| List | 1-indexed dynamic array | [1, 2, 3] |
| Map | Key-value dictionary | { name: "Alice" } |
| Vector | f32 array for AI/embeddings | Vector([1.0, 2.0]) |
| Option | Some(value) or None | Some(42), None |
| WeakRef | Weak reference | WeakRef(object) |
| TaskHandle | Concurrent task handle | From Do in background |
| Error | Error object | From Try/Catch |
Number (Default)
Arbitrary precision decimal - the default numeric type.
Story:
# Mathematical honesty
Result is 0.1 + 0.2
Print Result # 0.3 ✓
# Large integers
BigInt is 123456789123456789123456789
Print BigInt
# Decimals
Price is 19.99
Tax is Price * 0.08
Total is Price + Tax
Print Total # 21.5892 (exact)
When to use: Financial calculations, counters, user-facing math, learning/teaching.
Properties:
.Sign- Returns -1, 0, or 1
See Numbers for details.
FastNumber
IEEE 754 f64 float - for performance-critical code.
Story:
# Explicit creation
Speed is FastNumber(299792458.0)
Time is FastNumber(2.5)
Distance is Speed * Time
# About 10x faster than Number
# But float errors return:
Test is FastNumber(0.1) + FastNumber(0.2)
Print Test # 0.30000000000000004
When to use: Physics simulations, graphics, games, high-frequency calculations.
Mixed operations:
Story:
A is 10 # Number
B is FastNumber(20.0) # FastNumber
C is A + B # Result is FastNumber (loses precision)
See FastNumber for details.
String
Grapheme-aware Unicode text - emoji counted correctly.
Story:
# Simple string
Name is "Alice"
# With escape sequences
Message is "Line 1\nLine 2\tTabbed"
# Multiline (triple quotes)
Text is """This is
a multiline
string"""
# Concatenation
Greeting is "Hello, " + Name
Print Greeting # "Hello, Alice"
# Emoji counted correctly
Emoji is "👨👩👧👦"
Print Emoji.Length # 1 ✓ (one grapheme cluster)
Properties:
.Length- Number of graphemes (human-perceived characters).ByteSize- Number of UTF-8 bytes
Methods:
.ToUpper- Convert to uppercase.ToLower- Convert to lowercase.Trim- Remove leading/trailing whitespace.Contains- Check if substring exists.Slice(start, end)- Extract substring (1-based)
See Strings for details.
Boolean
True or False - for logical operations.
Story:
IsActive is True
IsDeleted is False
# From comparisons
IsAdult is Age >= 18
HasAccess is Role = "admin"
# Logical operations
CanEdit is IsActive and HasAccess
ShouldHide is IsDeleted or not IsActive
Values: True, False (case-sensitive)
See Boolean for details.
List
1-indexed dynamic array - the first element is at index 1.
Story:
# Create empty list
Items is []
# Create with values
Numbers is [1, 2, 3, 4, 5]
# Multiline
Colors is [
"red",
"green",
"blue"
]
# Access (1-based!)
First is Numbers[1] # 1
Third is Numbers[3] # 3
# Modify
Numbers[2] is 20
Print Numbers # [1, 20, 3, 4, 5]
# Add elements
Numbers.Add(6)
Print Numbers # [1, 20, 3, 4, 5, 6]
# Length
Count is Numbers.Length
Last is Numbers[Count]
Properties:
.Lengthor.Size- Number of elements
Methods:
.Add(item)- Append element.Remove(index)- Remove at index (1-based).Contains(item)- Check if element exists.Slice(start, end)- Extract sublist (1-based, inclusive).Sort()- Sort in ascending order.Sort("desc")- Sort in descending order.Reverse()- Reverse order.Clear()- Remove all elements
See Lists for details.
Map
Key-value dictionary - associative array.
Story:
# Create empty map
Config is {}
# Create with values
User is { name: "Alice", age: 25, email: "alice@example.com" }
# Multiline
Settings is {
host: "localhost",
port: 8080,
debug: True
}
# Access
Name is User["name"]
Age is User["age"]
# Modify
User["age"] is 26
# Add new key
User["role"] is "admin"
# Check if key exists
If User.HasKey("email"):
Print "Email: " + User["email"]
Properties:
.Sizeor.Length- Number of key-value pairs
Methods:
.HasKey(key)- Check if key exists.Keys()- Get list of all keys.Values()- Get list of all values.Remove(key)- Remove key-value pair.Clear()- Remove all entries
See Maps for details.
Vector
f32 array - for AI embeddings and numeric arrays.
Story:
# Create vector
Embedding is Vector([0.1, 0.2, 0.3, 0.4, 0.5])
# For machine learning / AI
UserEmbedding is Vector([/* floats */])
Use case: Storing embeddings from LLM API calls, neural network weights.
Option
Some(value) or None - explicit optionality, no null pointers.
Story:
# Create options
HasValue is Some(42)
NoValue is None
# Check
If HasValue.IsSome:
Print "Has value!"
If NoValue.IsNone:
Print "No value!"
# Extract value
If HasValue.IsSome:
Value is HasValue.Unwrap()
Print Value # 42
# Unwrap with default
Value1 is HasValue.UnwrapOr(0) # 42
Value2 is NoValue.UnwrapOr(0) # 0 (default)
Properties:
.IsSome- Returns True if has value.IsNone- Returns True if no value
Methods:
.Unwrap()- Extract value (crashes if None!).UnwrapOr(default)- Extract value or return default
See Option Types for details.
WeakRef
Weak reference - reference that doesn’t prevent garbage collection.
Story:
# Create strong reference
Items is [1, 2, 3]
# Create weak reference
WeakItems is WeakRef(Items)
# Check if still valid
If WeakItems.IsValid:
Print "Reference still alive"
# Restore is WeakItems.Get() # Not yet implemented
Else:
Print "Reference was collected"
Properties:
.IsValid- Returns True if reference still alive
Use case: Caching, avoiding circular references.
See Weak References for details.
TaskHandle
Concurrent task handle - returned by Do in background.
Story:
# Spawn background task
Task is Do in background:
Result is ExpensiveCalculation()
Return Result
# Do other work...
Print "Working..."
# Wait for result
Value is Task.Await()
Print "Result: " + Value
Methods:
.Await()- Wait for task to complete and get result
See Concurrency for details.
Error
Error object - from Try/Catch blocks.
Story:
Try:
Result is RiskyOperation()
Catch Error:
Print "Error: " + Error.Message
Print "Type: " + Error.Type
Properties:
.Message- Error message string.Type- Error type/category
See Error Handling for details.
Type Conversion
Automatic Conversions
Story:
# Number to String (in concatenation)
Age is 25
Message is "Age: " + Age # "Age: 25"
# Boolean to String
IsActive is True
Status is "Active: " + IsActive # "Active: True"
Explicit Conversions
Story:
# String to Number
Text is "42"
Num is Int.Parse(Text) # Use with Try/Catch!
# FastNumber to Number
Fast is FastNumber(3.14)
Regular is Number(Fast)
Type Checking
SFX doesn’t have explicit type checking operators, but you can check values:
Story:
Value is 42
# Check for specific values
If Value = 42:
Print "It's 42!"
# Check empty list
Items is []
If Items.Length = 0:
Print "Empty list"
# Check empty string
Name is ""
If Name = "":
Print "Empty string"
Default Values (Concept Fields)
When declaring fields in Concepts without initialization:
Concept: Example
NumberField # 0
StringField # ""
BoolField # False
ListField # []
MapField # {}
OptionField # None
Story:
Create Example Called Ex
Print Ex.NumberField # 0
Print Ex.BoolField # False
Print Ex.ListField.Length # 0
Nested Types
List of Lists
Story:
Grid is [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# Access: row 2, column 3
Value is Grid[2][3] # 6
List of Maps
Story:
Users is [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
{ name: "Charlie", age: 35 }
]
FirstUser is Users[1]
Name is FirstUser["name"] # "Alice"
Map with Lists
Story:
Data is {
users: ["Alice", "Bob"],
scores: [95, 87],
active: True
}
Users is Data["users"]
FirstUser is Users[1] # "Alice"
Best Practices
1. Use Number by Default
# Good - exact math
Price is 19.99
Tax is Price * 0.08
2. Use FastNumber for Performance
# Physics/graphics - performance matters
Concept: Particle
X, Y, VX, VY
To Update with DT:
FDT is FastNumber(DT)
Set This.X to This.X + (This.VX * FDT)
3. Use Option for Maybe-Present Values
# Good - explicit optionality
Concept: User
ProfilePicture # Option type
# Bad - using empty string to mean "no value"
ProfilePicture is "" # Confusing
4. Keep Lists Homogeneous
# Good - all same type
Numbers is [1, 2, 3, 4, 5]
Names is ["Alice", "Bob", "Charlie"]
# Bad - mixed types
Mixed is [1, "two", True] # Confusing
5. Use Descriptive Map Keys
# Good
User is { name: "Alice", email: "alice@example.com", age: 25 }
# Less clear
User is { n: "Alice", e: "alice@example.com", a: 25 }
Summary
- Number - Default, arbitrary precision, mathematically honest
- FastNumber - Performance, IEEE 754 float
- String - Grapheme-aware Unicode, emoji = 1 character
- Boolean - True/False
- List - 1-based dynamic array
- Map - Key-value dictionary
- Option - Explicit optionality (no null!)
- WeakRef - Weak references for caching
- Vector - f32 arrays for AI/ML
Next: Operators - Arithmetic, comparison, and logical operators
Numbers
FastNumber
Strings
Boolean
Lists
Maps
Option Types
Weak References
Operators
SFX provides operators for arithmetic, comparison, logical operations, and more.
Arithmetic Operators
Perform mathematical calculations:
| Operator | Operation | Example | Result |
|---|---|---|---|
+ | Addition | 10 + 5 | 15 |
- | Subtraction | 10 - 5 | 5 |
* | Multiplication | 10 * 5 | 50 |
/ | Division | 10 / 5 | 2 |
% | Modulo (remainder) | 10 % 3 | 1 |
Story:
A is 10
B is 3
Sum is A + B # 13
Diff is A - B # 7
Product is A * B # 30
Quotient is A / B # 3.333...
Remainder is A % B # 1
Print "Sum: " + Sum
Print "Product: " + Product
Print "Remainder: " + Remainder
Mathematical Honesty
Remember, SFX uses arbitrary precision by default:
Story:
Result is 0.1 + 0.2
Print Result # 0.3 ✓ (not 0.30000000000000004)
Price is 19.99
Tax is Price * 0.08
Total is Price + Tax
Print Total # 21.5892 (exact)
Unary Minus
Story:
X is 10
Y is -X # -10
Z is -Y # 10
Comparison Operators
Compare values and return Boolean results:
| Operator | Operation | Example | Result |
|---|---|---|---|
= | Equal to | 5 = 5 | True |
<> | Not equal to | 5 <> 3 | True |
< | Less than | 3 < 5 | True |
> | Greater than | 5 > 3 | True |
<= | Less than or equal | 5 <= 5 | True |
>= | Greater than or equal | 5 >= 3 | True |
Story:
Age is 25
MinAge is 18
MaxAge is 65
IsAdult is Age >= MinAge # True
IsSenior is Age >= MaxAge # False
IsEqual is Age = 25 # True
IsNotEqual is Age <> 30 # True
String Comparison
Story:
Name1 is "Alice"
Name2 is "Bob"
Name3 is "Alice"
Same is Name1 = Name3 # True
Different is Name1 <> Name2 # True
# Lexicographic comparison
Before is Name1 < Name2 # True ("Alice" < "Bob")
Boolean Comparison
Story:
Active is True
Deleted is False
Same is Active = True # True
Different is Active <> Deleted # True
Logical Operators
Combine Boolean expressions:
| Operator | Operation | Example | Result |
|---|---|---|---|
and | Logical AND | True and False | False |
or | Logical OR | True or False | True |
not | Logical NOT | not True | False |
Story:
Age is 25
HasLicense is True
HasCar is False
# AND - both must be true
CanDrive is Age >= 18 and HasLicense # True
# OR - at least one must be true
HasTransport is HasCar or HasBike # Depends on HasBike
# NOT - inverts boolean
IsUnderAge is not (Age >= 18) # False
# Combined
CanRent is Age >= 21 and HasLicense and not HasCar
Truth Tables
AND:
| A | B | A and B |
|---|---|---|
| True | True | True |
| True | False | False |
| False | True | False |
| False | False | False |
OR:
| A | B | A or B |
|---|---|---|
| True | True | True |
| True | False | True |
| False | True | True |
| False | False | False |
NOT:
| A | not A |
|---|---|
| True | False |
| False | True |
Short-Circuit Evaluation
SFX uses short-circuit evaluation for and and or:
Story:
# AND - stops if first is False
Result is False and ExpensiveCheck() # ExpensiveCheck() not called
# OR - stops if first is True
Result is True or ExpensiveCheck() # ExpensiveCheck() not called
String Operators
Concatenation (+)
Story:
FirstName is "Alice"
LastName is "Smith"
FullName is FirstName + " " + LastName
Print FullName # "Alice Smith"
# Auto-conversion with numbers
Age is 25
Message is "Age: " + Age
Print Message # "Age: 25"
Contains
Story:
Text is "Hello, World!"
If Text contains "World":
Print "Found!"
If Text contains "Goodbye":
Print "This won't print"
List Operators
Contains
Story:
Numbers is [1, 2, 3, 4, 5]
If Numbers contains 3:
Print "3 is in the list"
If not (Numbers contains 10):
Print "10 is not in the list"
Operator Precedence
Operators are evaluated in this order (highest to lowest):
- Parentheses -
() - Unary minus -
-X - Multiplication, Division, Modulo -
*,/,% - Addition, Subtraction -
+,- - Comparison -
<,>,<=,>=,=,<> - Logical NOT -
not - Logical AND -
and - Logical OR -
or
Examples
Story:
# Multiplication before addition
Result is 2 + 3 * 4
Print Result # 14 (not 20)
# Use parentheses to override
Result is (2 + 3) * 4
Print Result # 20
# Comparison before logical
Result is 5 > 3 and 10 < 20
Print Result # True
# Complex expression
X is 10
Y is 5
Z is 3
Result is X + Y * Z > 20 and X / Y = 2
# (10 + (5 * 3)) > 20 and (10 / 5) = 2
# (10 + 15) > 20 and 2 = 2
# 25 > 20 and True
# True and True
# True
Print Result # True
Best Practices
1. Use Parentheses for Clarity
# Less clear
Result is A + B * C / D
# Better
Result is A + ((B * C) / D)
2. Compare with Same Types
# Good
Age is 25
IsAdult is Age >= 18 # Number to Number
# Potentially confusing
IsAdult is Age >= "18" # Number to String - avoid!
3. Use Descriptive Names for Boolean Expressions
# Good
IsEligible is Age >= 18 and HasLicense
CanProceed is IsAuthenticated and not IsBlocked
# Less clear
Check is Age >= 18 and HasLicense
Flag is IsAuthenticated and not IsBlocked
4. Break Complex Expressions
# Complex - hard to read
If Age >= 18 and HasLicense and not HasCar and Income > 30000 and Score > 700:
ApproveRental()
# Better - broken down
IsAdult is Age >= 18
HasRequirements is HasLicense and not HasCar
IsFinanciallyStable is Income > 30000 and Score > 700
IsEligible is IsAdult and HasRequirements and IsFinanciallyStable
If IsEligible:
ApproveRental()
5. Avoid Double Negatives
# Confusing
If not (not IsActive):
DoSomething()
# Better
If IsActive:
DoSomething()
Common Patterns
Range Check
Story:
Age is 25
MinAge is 18
MaxAge is 65
InRange is Age >= MinAge and Age <= MaxAge
Print InRange # True
Either-Or Check
Story:
Role is "admin"
HasAccess is Role = "admin" or Role = "moderator"
Print HasAccess # True
Safe Division
Story:
A is 10
B is 0
If B <> 0:
Result is A / B
Print Result
Else:
Print "Cannot divide by zero"
Default Value Pattern
Story:
UserInput is ""
# Use default if empty
Name is If UserInput <> "" then UserInput else "Guest"
Print Name # "Guest"
Null-Coalescing Pattern (with Option)
Story:
OptionalValue is None
# Use UnwrapOr for default
Value is OptionalValue.UnwrapOr(42)
Print Value # 42
Examples
Calculator
Concept: Calculator
To Calculate with Operator and A and B:
When Operator:
is "+":
Return A + B
is "-":
Return A - B
is "*":
Return A * B
is "/":
If B = 0:
Print "Error: Division by zero"
Return 0
Else:
Return A / B
Otherwise:
Print "Unknown operator"
Return 0
Story:
Create Calculator Called Calc
Result1 is Calc.Calculate with "+" and 10 and 5
Print "10 + 5 = " + Result1 # 15
Result2 is Calc.Calculate with "*" and 10 and 5
Print "10 * 5 = " + Result2 # 50
Grade Calculator
Story:
Score is 85
# Determine grade
Grade is ""
If Score >= 90:
Grade is "A"
Else If Score >= 80:
Grade is "B"
Else If Score >= 70:
Grade is "C"
Else If Score >= 60:
Grade is "D"
Else:
Grade is "F"
Print "Score: " + Score
Print "Grade: " + Grade
Validation
Concept: Validator
To ValidateAge with Age:
IsValid is Age >= 0 and Age <= 150
Return IsValid
To ValidateEmail with Email:
HasAt is Email contains "@"
HasDot is Email contains "."
NotEmpty is Email <> ""
IsValid is NotEmpty and HasAt and HasDot
Return IsValid
Story:
Create Validator Called V
If V.ValidateAge with 25:
Print "Valid age"
If V.ValidateEmail with "alice@example.com":
Print "Valid email"
Price Calculator
Story:
BasePrice is 100
Quantity is 3
TaxRate is 0.08
DiscountRate is 0.10
# Calculate subtotal
Subtotal is BasePrice * Quantity
# Apply discount
Discount is Subtotal * DiscountRate
AfterDiscount is Subtotal - Discount
# Calculate tax
Tax is AfterDiscount * TaxRate
# Final total
Total is AfterDiscount + Tax
Print "Subtotal: $" + Subtotal # $300
Print "Discount: $" + Discount # $30
Print "After Discount: $" + AfterDiscount # $270
Print "Tax: $" + Tax # $21.60
Print "Total: $" + Total # $291.60
Summary
- Arithmetic:
+,-,*,/,% - Comparison:
=,<>,<,>,<=,>= - Logical:
and,or,not - String:
+(concatenation),contains - List:
contains - Precedence: Use parentheses for clarity
- Mathematical honesty: Exact decimal arithmetic by default
Next: Comments - Documenting your code