Skip to main content

Naming Conventions

Epsil spells the standard library in lowercase: sin, map, isPrime, pi. Every function and constant of the library also answers to its MathJSON name, with an initial capital: Sin, Map, IsPrime, Pi. The two spellings name the same thing.

sin(pi / 2)
// ➔ 1
Sin(Pi / 2)
// ➔ 1
map(sin, [0, pi / 2])
// ➔ [0, 1]

The lowercase spelling is the style of the language, and it is the spelling Epsil writes: the serializer (serializeEpsil), the format command, the --epsil output mode and the snippet a diagnostic quotes all write a library name in lowercase (sin(x), map(sin, xs), pi). A program that binds the lowercase spelling itself (let sin = 3, a parameter named pi) is written with the capitalized name for that library member, so the text reads back as the same program. The capitalized spelling is what MathJSON uses, and what an engine error names.

How the spelling is formed​

The lowercase spelling of a library name lowercases its first letter: Floor is floor, IsPrime is isPrime, StringJoin is stringJoin, GoldenRatio is goldenRatio. When the name starts with several capital letters, the whole run is lowercased and the last letter of the run stays a capital when it starts the next word: GCD is gcd, LCM is lcm, LUDecomposition is luDecomposition, NDSolve is ndSolve.

The Standard Library page lists both spellings of every definition.

Names with no lowercase spelling​

A library name has no lowercase spelling when Epsil already has a way to write it:

  • Operators the language writes as symbols: Add is +, Power is ^, Pipe is |>, Equal is ==, And is &&, Element is in, Range is .., and so on for the whole operator table.
  • Constructs with their own syntax: If and Which are if/else, Match is match, Loop is for and while, Function is => and function, Declare is let and const, Block is { … }, Typed is x: T, Spread is ...xs, At is xs[i].
  • Literals: List is […], Tuple is (a, b), Set is {…}, Dictionary is {k: v}, String is "…", True and False are true and false, NaN and Infinity are literal words.
  • Single-letter names: D and N stay capitalized, because d and n are ordinary variable names.
  • Relation glyphs with no reading as a function (Approx, Tilde, Precedes, PlusMinus, …): write them through a LaTeX island.
  • Engine-internal heads that a program never writes (ErrorCode, RuntimeError, Signature, …).

Writing one of these in lowercase is an unknown call, reported with a did-you-mean suggestion when a close name exists.

User names and shadowing​

User-defined variables, functions, and types are lowercase too: total, area, type point = …. Nothing in the parser or the engine enforces a case; the library spellings and the user names share one namespace and resolve by scope, not by case.

A user binding shadows a library name for the rest of its scope, whichever spelling the library name has: a let, a const, a parameter, a loop variable, a match pattern, or a function definition.

let sum = 0
sum + 1
// ➔ 1
function mean(x) { 42 }
mean(7)
// ➔ 42
[1, 2, 3] |> map(count => count * 2)
// ➔ [2, 4, 6]

A bare library name that nothing shadows IS the library definition, in every position: mean alone is the Mean function, so mean + 1 is a type error rather than a sum with an unknown number. Declare the variable first.

let mean = 5
mean + 1
// ➔ 6

The constants e and i are lowercase library values already: e^2 is the exponential, i^2 is -1, and 1 + 2i is a complex number. They shadow like any other name — let e = 3; e^2 is 9.

The capitalized spelling shadows the same way: let Pi = 3 makes Pi the number 3 for the rest of its scope, and function Square(x) { x + 100 } makes Square(3) call that function. Shadowing a name that an operator builds changes the operator too: + is Add, so a user Add is what + calls in its scope, and a definition such as function Add(x, y) { x + y } calls itself without end. Compiled code does not use a shadowed library operator: the call is interpreted instead.

The absence markers Nothing, Missing and Undefined are the exception: they cannot be rebound, and a binding of one of them is an error (absence-marker-binding). The engine recognizes them by their name — it drops Nothing from an argument list and reads a Missing operand as absent — so a binding could never behave like the value it holds. In a match, test for a marker with == Missing: a bare Missing there would be a new variable.

To name a raw symbol that happens to spell a library name, use the verbatim form: `sin` is the symbol sin, not the sine function.

Glyph Aliases​

A few mathematical glyphs are input aliases for library symbols, canonicalized at the lexer — every position (expression, parameter, binding, match pattern) treats the glyph exactly like its ASCII spelling, and serialization writes the library spelling (pi for π):

GlyphSymbol
πPi
∞Infinity
ⅈImaginaryUnit
ⅇExponentialE
∅EmptySet
⧝ComplexInfinity
ℝRealNumbers
ℤIntegers
ℚRationalNumbers
ℕNonNegativeIntegers
ℂComplexNumbers
∫Integrate
∑Sum
∏Product
3.1 ∈ ℝ
// ➔ True
∫(1/x, x)
// ➔ Integrate(1/x, x)

Note the doublestruck ⅈ/ⅇ (U+2148/U+2147), not the ordinary letters: i and e are the lowercase library constants described above. To name a raw symbol that happens to be a glyph, use the verbatim form (`π`).

In a type annotation the number-set glyphs name the type, not the set constant: c: ℝ is c: real, n: ℕ is n: integer<0..>. See Types.

Subscripts​

A run of subscript letters and digits directly after a name is part of the name, spelled with an underscore — the same name the LaTeX x_n produces:

WrittenSymbol
xₙx_n
a₁a_1
a₁₂a_12
xᵢⱼx_ij

So xₙ can be declared, assigned, matched and passed exactly like x_n, and let xₙ = 3 followed by x_n reads the same binding. A subscript that holds a sign or a parenthesis is not a name: xₖ₊₁ is the expression Subscript(x, k + 1). Superscripts never join a name — x² is x^2; see Superscripts and subscripts.