Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 LieThe Truth (SFX)
0.1 + 0.2 ≠ 0.30.1 + 0.2 = 0.3 (arbitrary precision)
List[0] is firstList[1] is first (1-based indexing)
Null pointer errorsNo null (safe defaults: 0, “”, False, [])
“👨‍👩‍👧‍👦”.Length = 7“👨‍👩‍👧‍👦”.Length = 1 (grapheme clustering)
Objects are staticContext-oriented (Situations modify behavior)
Manual cache updatesReactive 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:

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

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

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 .Length property

7. Control Flow

If This.Items.Length = 0:
    Print "No tasks yet!"
Else:
    For each Item in This.Items:
        Item.Display
  • If/Else for conditionals
  • For each for 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:

  1. Get the item (creates a copy)
  2. Modify the copy
  3. 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:

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 of name = "Alice"
  • Clear keywords: Repeat 10 times instead of for(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:

  1. LLMs write a lot of code - SFX’s natural syntax is easier for AI to generate correctly
  2. Correctness matters - When AI generates financial calculations, 0.1 + 0.2 should equal 0.3
  3. Rapid iteration - JIT compilation means you get interpreter speed + compiler performance

Comparison

FeatureSFXPythonJavaScriptJava
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?


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 1 with 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

OperationNumber (exact)FastNumber (float)
Addition100%~1000% (10x faster)
Multiplication100%~1000%
Division100%~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:

  1. Variables can be null
  2. You forget to check for null
  3. 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

TypeDefault ValueExample
Number0Age defaults to 0
FastNumber0.0Speed defaults to 0.0
String“”Name defaults to ""
BooleanFalseActive defaults to False
List[]Items defaults to []
Map{}Config defaults to {}
OptionNoneResult 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

LanguageNull Handling
Javanull everywhere, can crash anytime
JavaScriptnull and undefined, both cause crashes
PythonNone, causes crashes
RustOption<T>, must handle (like SFX)
KotlinNullable types T?, must check
SFXSafe 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 OR e + 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:

  1. Code points - Individual Unicode values (U+0041, U+1F600, etc.)
  2. Code units - UTF-8 bytes, UTF-16 words
  3. 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“👨‍👩‍👧‍👦”.LengthMethod
SFX1 ✓Grapheme clusters
Python7Code points
JavaScript11UTF-16 code units
Java11UTF-16 code units
Go25UTF-8 bytes
Rust25UTF-8 bytes (default)
Swift1 ✓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:

CategoryKeywords
StructureStory, Concept, Situation
Assignmentis, Set, to
Control FlowIf, Else, Repeat, For, each, in, When, Otherwise
Object CreationCreate, Called
MethodsTo, with, and, Return
ContextSwitch, on, off, Adjust, Proceed
Error HandlingTry, Catch, Always
ConcurrencyDo, in, background
LogicTrue, False, and, or, not
LoopsBreak, 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
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

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:

  1. Use PascalCase - Start with uppercase letter
  2. Can contain letters, numbers, and underscores
  3. Case-sensitive - Name and name are different variables
  4. Cannot start with a number
  5. 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 is for assignment: Name is "Alice"
  • Variables are case-sensitive: Namename
  • 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

TypeDescriptionExample
NumberArbitrary precision decimal42, 3.14
FastNumberIEEE 754 float (f64)FastNumber(3.14)
StringGrapheme-aware text"Hello", """Multi-line"""
BooleanTrue or FalseTrue, False
List1-indexed dynamic array[1, 2, 3]
MapKey-value dictionary{ name: "Alice" }
Vectorf32 array for AI/embeddingsVector([1.0, 2.0])
OptionSome(value) or NoneSome(42), None
WeakRefWeak referenceWeakRef(object)
TaskHandleConcurrent task handleFrom Do in background
ErrorError objectFrom 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:

  • .Length or .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:

  • .Size or .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:

OperatorOperationExampleResult
+Addition10 + 515
-Subtraction10 - 55
*Multiplication10 * 550
/Division10 / 52
%Modulo (remainder)10 % 31
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:

OperatorOperationExampleResult
=Equal to5 = 5True
<>Not equal to5 <> 3True
<Less than3 < 5True
>Greater than5 > 3True
<=Less than or equal5 <= 5True
>=Greater than or equal5 >= 3True
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:

OperatorOperationExampleResult
andLogical ANDTrue and FalseFalse
orLogical ORTrue or FalseTrue
notLogical NOTnot TrueFalse
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:

ABA and B
TrueTrueTrue
TrueFalseFalse
FalseTrueFalse
FalseFalseFalse

OR:

ABA or B
TrueTrueTrue
TrueFalseTrue
FalseTrueTrue
FalseFalseFalse

NOT:

Anot A
TrueFalse
FalseTrue

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):

  1. Parentheses - ()
  2. Unary minus - -X
  3. Multiplication, Division, Modulo - *, /, %
  4. Addition, Subtraction - +, -
  5. Comparison - <, >, <=, >=, =, <>
  6. Logical NOT - not
  7. Logical AND - and
  8. 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

Comments

Comma-Separated Syntax

If/Else

When/Is/Otherwise

Repeat Times

Repeat While

For Each

Break and Continue

Return

Concepts (Classes)

Fields

Methods

Method Parameters

This Keyword

Creating Instances

Set Statement

Situations

Adjustments

Switch On/Off

Proceed

Use Cases

When Observers

Self-Healing Data

Cascading Updates

Recursion Guard

Use Cases

Try/Catch/Always

Error Objects

Best Practices

Do in Background

Task Handles

Channels

Thread Safety

Overview

File Operations

Data Parsing

JSON

XML

HTML

CSV

TOML

Networking

HTTP

WebSocket

TCP

UDP

System

Environment

Time

Math

LLM Integration

Overview

How It Works

Profiling System

Optimization Tiers

Performance Benchmarks

Supported Features

Limitations

Hello World

Mathematical Operations

Working with Lists

Object-Oriented Examples

Context-Oriented Examples

Reactive Observers

File I/O

HTTP Requests

Concurrency

Physics Simulation

Performance Tips

Memory Management

Debugging

Testing

Project Structure

Keyword Reference

Operator Precedence

Standard Library API

Error Messages

Command Line Interface

Contributing Guide

Code of Conduct

Security Policy

Development Setup

Architecture

Comparison with Other Languages

Migration Guide

FAQ

Glossary

Changelog