FluentAI Language Specification

1. Core Philosophy

FluentAI is a general-purpose programming language designed for clarity, expressiveness, and safety. Its core philosophy is that all operations should, whenever possible, be expressed as a linear, left-to-right chain of transformations.

This is achieved by making method chaining the primary mode of expression, enhanced by powerful, inline lambda expressions (param => ...) that allow for complex logic without breaking the fluent flow. The syntax prioritizes consistency, unambiguity, and human ergonomics, making it ideal for both developers and AI-driven tooling.

FluentAI combines the familiarity of C#-style syntax with functional programming features, making it an easy transition for C# developers while providing modern language capabilities.

2. Lexical Structure

This section defines the basic grammar and tokens of the language.

Comments

// Single-line comment
/* Multi-line comment */

Identifiers & Naming Conventions

Keywords

Definitions: private, public, function, struct, enum, trait, as, type, actor, effect, macro, extern

Control Flow: if, else, match, case, for, while, in, let, const

Concurrency: async, await, parallel, handle

Error Handling: try, catch, finally

Modules: use, mod

Other: true, false, self, super, unsafe, dyn, with, perform

Operators

3. Statement Termination and Semicolons

FluentAI uses semicolons to clearly mark the end of statements, following a similar model to C# and other C-family languages. This approach eliminates ambiguity and makes the code structure explicit.

Semicolon Rules

Statements that REQUIRE semicolons:

Constructs that DO NOT require semicolons:

Examples

// Statements requiring semicolons
let name = "Alice";
let age = 30;
name := "Bob";
print_user(name, age);

// Control flow - no semicolon after blocks
if (age > 18) {
    print("Adult");
    process_adult(name);
} else {
    print("Minor");
}

// Last expression in block - no semicolon (implicit return)
let result = {
    let x = calculate();
    let y = transform(x);
    x + y  // No semicolon - this is the return value
};

// Function definition - no semicolon
private function greet(name: string) {
    $(f"Hello, {name}!").print();
}

4. Primitive Types & Literals

Primitive Types

Collection Literals

String Formatting

F-strings are used for easy interpolation:

let name = "Alice";
let message = f"Hello, {name}!"; // "Hello, Alice!"

Console Output

The $(...) construct wraps a string in a "Printable" object. This is the idiomatic way to handle console I/O:

$("This will be printed to the console.").print();

let name = "World";
$(f"Hello, {name}!").print(); // Works with formatted strings

5. Modules and Visibility

Modules provide encapsulation and organization. Each .flc file is implicitly a module.

Module Declaration

// Optional module declaration at the top of a file
mod ModuleName;

// If no module declaration is provided, the module name defaults to the filename
// File: Stock.flc → module Stock
// File: price_service.flc → module price_service

Automatic Exports

Importing

// Import a constructor or primary export
use Stock;
use Portfolio;

// Import specific public items
use Stock::{from_json, to_json};
use Portfolio::{calculate_value, rebalance};

// Import all public items from a module
use PriceService::*;

// Import with aliasing
use VeryLongModuleName as VLMN;
use SomeLongFunctionName as short_name from Utils;

// Relative imports
use ./utils/Helper;  // From relative path
use ../shared/Common;  // From parent directory

Module Path Resolution

// Standard library modules use std:: prefix
use std::collections::List;
use std::io::File;

// Local modules use relative paths or module names
use ./models/User;  // Relative to current file
use Portfolio;      // Searches module path

Visibility

5.1 Object-Oriented Patterns

FluentAI supports object-oriented programming through closure patterns. This provides encapsulation and method chaining without requiring special class syntax.

Pattern Example

// Stock.flc
mod Stock;

public function Stock(symbol: string, shares: float, price: float) {
    // Private state - not accessible from outside
    let state = {
        "symbol": symbol,
        "shares": shares,
        "current_price": price
    };
    
    // Private helper functions
    let validate_shares = (shares) => shares >= 0.0;
    
    // Create the public interface
    let self = {
        "get_symbol": () => state.symbol,
        "get_shares": () => state.shares,
        "get_value": () => state.shares * state.current_price,
        
        "update_shares": (new_shares) => {
            if (validate_shares(new_shares)) {
                state.shares = new_shares;
            }
            self  // Return self for method chaining
        },
        
        "update_price": (new_price) => {
            if (new_price > 0.0) {
                state.current_price = new_price;
            }
            self
        },
        
        "to_json": () => {
            {"symbol": state.symbol, "shares": state.shares, "price": state.current_price}
        }
    };
    
    return self;
}

// Additional module-level functions
public function from_json(data) {
    Stock(data.symbol, data.shares, data.price)
}

Usage from Another Module

// main.flc
use Stock;
use Stock::{from_json};

let my_stock = Stock("AAPL", 100, 150.0);
my_stock
    .update_price(155.0)
    .update_shares(110);

let value = my_stock.get_value();  // 17,050.0
$(f"Stock value: ${value}").print();

// Using the additional function
let stock_data = {"symbol": "GOOGL", "shares": 50, "price": 2800.0};
let google = from_json(stock_data);

6. Definitions & Declarations

All top-level definitions start with a visibility keyword (public or private).

Functions

// Top-level function
public function create_user(name: string) -> User { ... }

// Private functions can be defined at top-level
private function helper() { ... }

// Note: Inside blocks/expressions, use let bindings for functions:
{
    let local_func = (x) => x * 2;
    local_func(5)
}

Structs (Value Types)

public struct User {
    id: Uuid,         // Private field
    pub name: string, // Public field
}

Enums

public enum RequestState { 
    Pending,
    Success(string),
    Error { code: int, message: string }
}

Traits (Interfaces)

public trait Serializable {
    private function to_json(self) -> string;
}

Trait Implementations

The as keyword is used to implement a trait for a type:

private User as Serializable {
    private function to_json(self) -> string {
        // implementation...
    }
}

7. Chaining, Lambdas, and Control Flow

The core of FluentAI is the fluent chain.

Implicit Iteration

Collection processing methods like .map, .filter, and .for_each_async can be called directly on collections. There is no need for an explicit .iter() call.

// Correct:
[1, 2, 3].map(x => x * 2)

// Incorrect (unnecessary):
[1, 2, 3].iter().map(x => x * 2)

Lambda Expressions

Lambdas are passed as arguments to methods. The => operator separates parameters from the body.

// Single parameter
users.filter(user => user.age > 18)

// Multiple parameters
numbers.reduce(0, (acc, item) => acc + item)

// No parameters
on_done(() => $("Task complete!").print())

Destructuring in Lambdas

users.map(({name, email}) => f"{name} <{email}>")

Control Flow as Chainable Expressions

let message = if (user.is_active) { "Active" } else { "Inactive" };

// The `match` expression is a powerful tool in chains.
let status_text = response
    .match()
        // For simple value mapping
        .case(200, "OK")
        .case(404, "Not Found")
        // For complex logic, the pattern binds variables for the lambda body.
        // Note the lambda `=> { ... }` has no parameters of its own.
        .case(Err(e), => {
            log_error(e);
            "An unexpected error occurred"
        })
        .get();

8. Traits & Polymorphism

Polymorphism is the ability to write code that operates on values of different types, achieved through traits.

Static Polymorphism via Generics

Zero-cost, compile-time polymorphism:

private function log_as_json(item: T) {
    item.to_json() |> print()
}

Dynamic Polymorphism via Trait Objects

Runtime flexibility using dyn<Trait>:

let ui_elements: List>> = [ ... ];
for element in ui_elements {
    element.draw(); // Call is dispatched at runtime
}

9. Concurrency

Async/Await

private async function fetch_user_data(id: Uuid) -> User {
    http.get(f"/users/{id}").await()
}

// Async blocks
let result = async { 
    let data = fetch_data().await();
    process(data)
}.await();

Actor Model

private actor Counter {
    count: int = 0;
    private handle Inc(amount: int) { self.count += amount; }
}

Channels and Message Passing

// Create a channel
let ch = channel();

// Send and receive (using macro syntax)
send!(ch, "message");
let msg = recv!(ch);

// Spawn concurrent tasks
spawn {
    let result = expensive_computation();
    send!(ch, result);
};

let result = recv!(ch);

10. Effects and Side Effect Management

Effects provide a structured way to handle side effects. The perform keyword executes effectful operations:

// Perform I/O operations
perform IO.print("Hello, World");
perform IO.println("With newline");
let input = perform IO.read_line();

// State management
perform State.set(42);
let value = perform State.get();

// Other effect types
perform Network.fetch(url);
perform Random.int(1, 100);
perform Time.now();

// Handle effects with custom handlers
handle {
    perform IO.print("This will be captured");
    perform State.set(42);
} with {
    IO.print(msg) => captured_messages.push(msg),
    State.set(value) => { current_state = value; }
}

11. Recursive Functions

Recursive functions can be defined using regular let bindings:

// Simple recursion
let factorial = (n) => {
    if (n <= 1) { 1 }
    else { n * factorial(n - 1) }
};

// Mutual recursion
let even = (n) => {
    if (n == 0) { true }
    else { odd(n - 1) }
};
let odd = (n) => {
    if (n == 0) { false }
    else { even(n - 1) }
};
Note: The current implementation may have limitations with recursive let bindings. For guaranteed support, use top-level function definitions.

12. Metaprogramming & Advanced Features

Derive Attributes

private struct User { ... }.derive(Debug, Clone)

Contract Specifications (Future Feature)

@contract {
    requires: n >= 0,
    ensures: result >= 1
}
private function factorial(n: int) -> int { ... }

Macros

// Macro invocation uses ! syntax
assert!(x > 0, "x must be positive");
println!("Value: {}", x);

// Custom macros (future feature)
macro rules! {
    // macro definition
}

FFI (Foreign Function Interface)

extern "C" {
    private function c_lib_function(input: i32) -> i32;
}

Effect Systems

private effect Database { ... }
private function get_user(id: Uuid).with(Database) { ... }

13. Complete Example: Console Application

This example demonstrates a simple console application that fetches user data from a mock API, showcasing many of the language's features working together.

// main.flc

// === Definitions ===
public struct User { pub id: int, pub name: string }
    .derive(Debug, Clone)

public enum ApiError { NotFound, Network(string) }
    .derive(Debug)

public trait ApiClient {
    private async function fetch_user(self, id: int) -> Result;
}

private struct MockApiClient { db: HashMap }

// === Implementations ===
private MockApiClient as ApiClient {
    private async function fetch_user(self, id: int) -> Result {
        tokio.sleep_ms(100).await();
        self.db.get(&id)
            .cloned()
            .ok_or(ApiError.NotFound)
    }
}

private MockApiClient {
    private function new() -> Self {
        let db = HashMap.new()
            .insert(1, User{id: 1, name: "Alice"})
            .insert(2, User{id: 2, name: "Bob"});
        Self{db: db}
    }
}

// === Logic Functions ===

// This function processes the data and returns a list of result strings.
private async function fetch_and_format_results(client: MockApiClient) -> List {
    [1, 3, 2] // List of IDs to fetch
        .map_async(id => {
            client.fetch_user(id)
                .await()
                .match()
                    // The pattern `Ok({name, ..})` binds the `name` variable.
                    .case(Ok({name, ..}), => f"Success! Found user: {name}")
                    // The pattern `Err(...)` handles the NotFound case.
                    .case(Err(ApiError.NotFound), => f"Handled Error: User with ID '{id}' was not found.")
                    .get() // Get the resulting string from the match expression
        })
        .await() // Wait for all async map operations to complete
}

// This function takes the results and prints them to the console.
private function print_results(results: List) {
    $("--- Processing Complete ---").print();
    results.for_each(result => $(result).print());
}

// === Main Application Entry Point ===
private async function main() {
    MockApiClient.new()
        .let(client => {
            fetch_and_format_results(client)
                .await()
                |> print_results()
        })
}

14. Multi-Module Example: Stock Portfolio Rebalancer

This example demonstrates how to structure a real application using FluentAI's module system.

The complete example spans multiple files showing how modules interact to create a portfolio rebalancing application. See the full specification document for the complete implementation including:

15. Current Limitations and Future Features

Parser Limitations

Future Features

Notes for Test Writers

When writing tests, prefer using features that are fully documented and implemented. Use #[ignore] attribute for tests that rely on future features.

Next: Continue with the Language Guide for practical examples and common patterns.