Expressions

Formatting rules for expressions: function calls, method chains, conditionals, lambdas, binary expressions, and bindings.

Function Calls

Inline Format

Used when the call fits in 100 characters:

let result = add(a: 1, b: 2)
let user = fetch_user(id: 42)
let msg = format(template: "Hello, {}", value: name)
let result = send_email(to: recipient, subject: title, body: content)

Broken Arguments

When the call exceeds 100 characters, break arguments one per line:

let result = send_notification(
    user_id: current_user,
    message: notification_text,
    priority: Priority.High,
    retry_count: 3,
)

Even single-argument calls break if exceeding 100:

let result = process(
    data: some_very_long_variable_name_that_pushes_past_the_limit,
)

Nested Calls

Nested calls break independently based on their own width:

// Inner call fits - stays inline
let result = process(
    data: transform(input: fetch(url: endpoint), options: defaults),
    config: settings,
)

// Inner call exceeds 100 - it breaks too
let result = process(
    data: transform(
        input: fetch(url: api_endpoint),
        options: default_transform_options,
        validator: schema_validator,
    ),
    config: settings,
)

Method Chains

Note: The method chain breaking infrastructure exists (MethodChainRule, collect_method_chain(), is_method_chain() in ori_fmt/src/rules/method_chain.rs) but is not yet invoked by the formatter’s emitter or breaking layers. The patterns below show the intended behavior once integration is complete.

Inline Format

Used when the chain fits in 100 characters:

let result = items.filter(x -> x > 0).map(x -> x * 2)

Broken Chain

When a chain exceeds 100 characters, break at every .:

let result = items
    .filter(x -> x > 0)
    .map(x -> x * 2)
    .fold(0, (a, b) -> a + b)

Important: Once any break is needed, all calls in the chain break. No partial breaking (all-or-nothing rule).

Chains Starting from Calls

let users = fetch_all_users()
    .filter(user -> user.is_active)
    .map(user -> user.name)
    .collect()

Mixed Access and Calls

let name = user.profile
    .preferences
    .display_name
    .unwrap_or(default: "Anonymous")

Conditionals

Inline Format

Used when the conditional fits in 100 characters:

let sign = if x > 0 then "positive" else "negative"
let abs = if n >= 0 then n else -n
let max = if a > b then a else b

Broken Format

Keep if cond then expr together, break at else:

let category =
    if value > 100 then "large"
    else "small"

Chained Else-If

let size =
    if n < 10 then "small"
    else if n < 100 then "medium"
    else "large"

Branch Bodies Break Independently

let result =
    if condition then compute_simple(x: value)
    else compute_with_many_args(
        input: data,
        fallback: default,
        options: config,
    )

Complex Conditions

Long conditions break before operators:

let valid =
    if is_authenticated(user)
        && has_permission(user, resource)
        && is_not_expired(token)
    then allow_access()
    else deny_access()

Lambdas

Single Parameter (No Parens)

x -> x + 1
items.map(x -> x * 2)
users.filter(user -> user.is_active)

Multiple Parameters (Parens Required)

(a, b) -> a + b
items.fold(0, (acc, x) -> acc + x)

Zero Parameters

() -> 42
() -> generate_id()

Typed Parameters

(x: int) -> int = x * 2
(input: str, config: Config) -> Result<Output, Error> = process(input, config)

Always-Stacked Body - Break After Arrow

Break after -> only when the body is an always-stacked construct (block, try, match):

let process = x -> {
    let doubled = x * 2;
    let validated = validate(doubled);
    validated
}

Lambda in Call Context

If the lambda fits, inline:

items.map(x -> x * 2)

If the lambda is too long, break the call argument, not the lambda itself:

// Long lambda - break the call
items.map(
    x -> compute_something_complex(input: x, options: defaults),
)

Break after -> only for always-stacked bodies:

// Always-stacked body - break after arrow
items.map(
    x -> {
        let y = x * 2;
        validate(y)
    },
)

Multiple Params with Complex Body

items.fold(
    0,
    (acc, x) -> {
        let computed = compute(x);
        acc + computed
    },
)

Binary Expressions

Inline Format

Used when the expression fits in 100 characters:

let result = a + b * c - d
let valid = x > 0 && x < 100
let combined = first || second && third

Break Before Operator

When exceeding 100 characters, break before the operator:

let result = first_value + second_value
    - third_value * fourth_value
    + fifth_value / sixth_value

let valid = is_authenticated(user)
    && has_permission(user, resource)
    && is_not_expired(token)

Precedence Preserved

Breaking doesn’t change precedence. Parentheses are preserved:

let result = (first_value + second_value)
    * (third_value - fourth_value)

Bindings

Simple Bindings

let x = 42
let $constant = 100
let name: str = "Alice"

Destructuring - Inline

let { x, y } = point
let (first, second) = pair
let [$head, ..tail] = items
let { position: { x, y }, velocity } = entity

Destructuring - Broken

When exceeding 100 characters:

let {
    id,
    name,
    email,
    preferences,
    created_at,
} = user

Binding with Long Value

If the value is long, it may break:

let result = compute_something_with_many_parameters(
    input: data,
    options: config,
    fallback: default_value,
)

Binding with Long Type Annotation

let handler: (Request, Context) -> Result<Response, Error> =
    create_handler(config: settings)

Indexing

Always inline:

let first = items[0]
let last = items[# - 1]
let value = map["key"]

Field Access

Always inline:

let x = point.x
let name = user.profile.name

Type Conversions

Always inline:

let f = 42 as float
let n = "42" as? int
let s = value as str