Idioms
None of this is enforced by the compiler; all of it is how Bridje is meant to read.
Layout
Section titled “Layout”Use the sugar
Section titled “Use the sugar”The s-expression forms always work, but idiomatic Bridje uses the sugared ones: f(a, b) for calls, name: blocks for anything with a body.
Reach for the parenthesised form only where the sugar can’t go — calling the result of an expression, for example: ((adder 1) 2).
(Call syntax on a call, adder(1)(2), is #146.)
Don’t combine the two: foo: a b already is (foo a b), so (foo: a b) is ((foo a b)).
Blocks for anything complex
Section titled “Blocks for anything complex”Keep a call on one line while it’s short and flat. Once it has more than a couple of arguments, or its arguments are themselves calls, give it a block:
and: gte(.term(req), .currentTerm(state)) or: isEmpty(.log(state)) eq(.from(req), .votedFor(state)) candidateLogUpToDate(state, req)The same goes for cond: and case: whose results are more than a single expression: put the result on the next line, indented.
A block runs to the end of its line, so a block in the middle of a line takes everything after it — keep a block last on its line.
Thread left to right
Section titled “Thread left to right”Prefer -> over nested calls when there are more than two steps, and write each step with parentheses — count(), not count — so it reads as a call:
->: cluster count() div(2) add(1)Top-level structure
Section titled “Top-level structure”A namespace can only use what’s defined above it, so a file reads bottom-up: members and types first, then helpers, then the public API.
Use //// comments to mark the sections:
//// Keysdecl: {.name Str, .age Int}
//// Typestag: Person{.name, .age}
//// Helpersdef: _isAdult(person) gte(.age(person), 18)
//// Public APIdef: greet(person) ...Leave a blank line between top-level forms.
A tight group of one-liners that belong together — a run of decl:s, say — can sit without them.
Naming
Section titled “Naming”camelCasefor functions and values:handleVoteRequest,startElection.PascalCasefor tags, enums and types:ServerState,VoteRequest..camelCasefor keys:.currentTerm,.knownLeader.lowercase.dottedfor namespaces:raft.server; a hyphen within a segment is fine for test namespaces,raft.server-test.- Acronyms are words:
handleRpc,parseJson— nothandleRPC. - Predicates take an
isprefix only where the name isn’t already a verb:isEmptyandisSame, butcontains,exists,hasNext. - No suffixes for effects or side-effecting functions:
log,send. What a function does to the world belongs in its type, not its name. _privateby convention. There’s no access control — nothing stops a caller using_helper— but the underscore says “not part of the API”.
The threading macros — ->, ?>, as->, cond-> — are the only names containing symbols; the hyphen there is half an arrow, not a word separator.
Host names are spelled as the host spells them.
A Java member is looked up by its name, so .toURI stays .toURI, even though a Bridje name would be toUri.
Bridje has three ways to model data; pick the least structure that does the job:
- A record when all that matters is the data — its keys and their values.
- A tag when a value needs an identity of its own: a
User, not just something with a.name. - An enum when a value is one of a fixed set of alternatives, each with its own data.
Functions transform data: records in, records out. Keep the logic in plain functions over plain data, and the outside world behind effects.
Keys are functions, so there’s rarely a need to wrap one: mapv(people, .name), not mapv(people, #: .name(it)).
Immutability
Section titled “Immutability”Values are immutable by default, and with returns a new record rather than changing the old one.
For now, there’s no in-place mutation of Bridje values at all — set has been taken out while mutability is rethought — so where you genuinely need mutable state, keep it in a host object, such as an AtomicReference.