Unfinished Ideas

Admittedly, many ideas for Cilia are not yet fully developed, but these really do need some more work.

T^ to Objects of Other Languages

We can redefine T^ for interoperability with other languages, e.g. garbage collected languages like C# and Java.

T^ is defined via type traits SharedPtrType:

T+ is defined via type traits UniquePtrType:

Exotic Operators (e.g. Unicode)

Logical / Bool Operators

It is also possible to use the mathematical symbols , , , , ¬ for and, or, nand, nor, not.

operator (Bool a) ∧ (Bool b) -> Bool { return a and b }
operator (Bool a) ∨ (Bool b) -> Bool { return a or b }
operator (Bool a) ⊼ (Bool b) -> Bool { return a nand b }
operator (Bool a) ⊽ (Bool b) -> Bool { return a nor b }
operator (Bool a) ⊻ (Bool b) -> Bool { return a xor b }
operator ¬(Bool a) -> Bool { return not a }

Vector / Matrix Operators

operator (Vec3 a) × (Vec3 b) -> Vec3       { ... }   // cross product (beware of confusion with the letter 'x')
operator (Vec a) ⋅ (Vec b) -> Float        { ... }   // dot / scalar / inner product

operator (Matrix a) ⊙ (Matrix b) -> Matrix { ... }   // Hadamard (element-wise) product
operator (Matrix a) ⊘ (Matrix b) -> Matrix { ... }   // Hadamard (element-wise) division
operator (Matrix a) ⊞ (Matrix b) -> Matrix { ... }   // element-wise addition ("boxplus")
operator (Matrix a) ⊟ (Matrix b) -> Matrix { ... }   // element-wise subtraction ("boxminus")
operator (Matrix a) ⊗ (Matrix b) -> Matrix { ... }   // tensor / Kronecker product

operator (Vec a) ⊕ (Vec b) -> Vec          { ... }   // direct sum: {1 2} ⊕ {3 4} -> {1 2 3 4}
operator ⊖(Vec a) -> Vec                   { ... }   // negation (unary)
operator (Vec a) ⊖ (Vec b) -> Vec          { ... }   // subtraction (binary)
operator (Signal a) ⊛ (Signal b) -> Signal { ... }   // convolution
operator (Signal a) ∗ (Signal b) -> Signal { ... }   // convolution (alternative)

func ∠(Vec a, b) -> Float        { ... }  // angle between two vectors
func ∠(Point3D a, b, c) -> Float { ... }  // angle between three points (vectors ab and bc)

Unclear, if these should have an epsilon (ε) value here. And then they would be function calls, not infix operators:

operator (Vec a) ⟂ (Vec b) -> Bool { ... }   // perpendicular / orthogonal
operator (Vec a) ∥ (Vec b) -> Bool { ... }   // parallel to
operator (Vec a) ∦ (Vec b) -> Bool { ... }   // not parallel to

Set Operators

Set operators fall into two precedence groups (see precedence diagram below). They are unrelated to scalar relational operators such as ==, !=, <, or > — there is no precedence ordering between the two domains, and meaningful expressions do not mix them.

Membership / subset, , , , , , , parse as binary infix operators returning Bool.

Algebraic, , parse as binary infix operators returning sets. They bind tighter than membership/subset (so x ∈ A ∪ B parses as x ∈ (A ∪ B)) but looser than range (.., ..<). Mixed algebraic expressions such as A ∪ B ∩ C require parentheses.

// Set membership
operator (T x) ∈ (Set<T> s) -> Bool { return s.contains(x) }
operator (T x) ∉ (Set<T> s) -> Bool { return not s.contains(x) }
operator (Set<T> s) ∋ (T x) -> Bool { return s.contains(x) }
operator (Set<T> s) ∌ (T x) -> Bool { return not s.contains(x) }

// Subset / superset
operator (Set<T> a) ⊆ (Set<T> b) -> Bool { return a.isSubsetOf(b) }
operator (Set<T> a) ⊇ (Set<T> b) -> Bool { return a.isSupersetOf(b) }
operator (Set<T> a) ⊂ (Set<T> b) -> Bool { return a.isProperSubsetOf(b) }
operator (Set<T> a) ⊃ (Set<T> b) -> Bool { return a.isProperSupersetOf(b) }
// Set algebra
operator (Set<T> a) ∪ (Set<T> b) -> Set<T> { return a.union(b) }
operator (Set<T> a) ∩ (Set<T> b) -> Set<T> { return a.intersection(b) }
operator (Set<T> a) ∖ (Set<T> b) -> Set<T> { return a.difference(b) }

Operator Precedence

Group names in the diagram below correspond to precedence declarations (see Custom Operators with Declared Precedence).

List of all currently known operators:

Operator precedence is a partial ordering, not a global one, as nobody can remember all these precedence levels.

See Carbon Expression Precedence:

Expressions are interpreted based on a partial precedence ordering. Expression components which lack a relative ordering must be disambiguated by the developer, for example by adding parentheses; otherwise, the expression will be invalid due to ambiguity. Precedence orderings will only be added when it’s reasonable to expect most developers to understand the precedence without parentheses.

Also see Circle simpler_precedence

In the diagram below, nodes higher up bind tighter; arrows point from looser toward tighter binding.

%%{init: {'themeVariables': {'fontFamily': 'monospace'}}}%%
graph BT
    primary["Primary
(…)
{…}
x"]

    suffixOps --> primary
    suffixOps{"Postfix
x.y
x.(…)
x->y
x->(…)
x(…)
x[y]"}

    qualifiedType --> suffixOps
    qualifiedType["TypeQualifier
const T"]

    pointerType --> qualifiedType
    pointerType{"PointerType
T*"}

    pointer --> suffixOps
    pointer{"Indirection
*x
&x"}

    prefixArithmetic & complement & incDec --> pointer
    complement["PrefixBitwise
 ^x
~x"]
    prefixArithmetic["PrefixArithmetic
-x
+x
√x
∛x
∜x
⊖x"]
    incDec["IncrementDecrement
++x
--x"]

    unary --> pointerType & prefixArithmetic & complement
    unary((" "))


    power --> unary
    power[\"Power
x ** y"\]

    multiplication --> power
    multiplication[/"Multiplication
x * y
x / y
x × y
x ⋅ y
x ⊙ y
x ⊘ y
x ⊗ y
x ⊛ y
x ∗ y"/]
    addition --> multiplication
    addition[/"Addition
x + y
x - y
x ⊞ y
x ⊟ y
x ⊕ y
x ⊖ y"/]

    modulo & rotate & shiftLeft & shiftRight & bitwiseAnd & bitwiseOr & bitwiseXor ---> unary
    modulo["Modulo
x % y"]
    rotate["Rotation
x <<< y
x >>> y"]
    shiftLeft[/"ShiftLeft
x << y"/]
    shiftRight[/"ShiftRight
x >> y"/]
    bitwiseAnd[/"BitwiseAnd
x & y"/]
    bitwiseOr[/"BitwiseOr
x | y"/]
    bitwiseXor[/"BitwiseXor
x ^ y"/]

    binaryOps --> addition & modulo & rotate & shiftLeft & shiftRight & bitwiseAnd & bitwiseOr & bitwiseXor
    binaryOps((" "))

    %% Ranges bind looser than arithmetic/bitwise, tighter than the relational operators
    range --> binaryOps
    range["RangeFormation
x .. y
x ..< y"]

    equality & comparison & parallel --> range
    equality["Equality
x == y
x != y
x ≠ y"]
    comparison["Comparison
x <=> y
x < y
x > y
x <= y
x >= y
x ≤ y
x ≥ y"]
    parallel["Parallel
x ⟂ y
x ∥ y
x ∦ y"]

    setUnion & setIntersection & setDifference --> range
    setUnion[/"Union
x ∪ y"/]
    setIntersection[/"Intersection
x ∩ y"/]
    setDifference[/"SetDifference
x ∖ y"/]

    setOps --> setUnion & setIntersection & setDifference
    setOps((" "))
    membership & subset --> setOps

    membership["Membership
x ∈ y
x ∉ y
x ∋ y
x ∌ y"]
    subset["Subset
x ⊆ y
x ⊇ y
x ⊂ y
x ⊃ y"]

    %% Use a longer arrow here to put `not` next to other unary operators
    not ---------> suffixOps
    not["LogicalNegation
not x
!x
¬x"]


    logicalOperand --> equality & comparison & membership & subset & parallel & not
    logicalOperand((" "))


    %% Currently "as" is not yet fully defined, and it obscures the graph layout somehow...
    %% as ------> unary
    %% as["Cast
%%x as T"]

    and & or & xor & nandNor & andSymbol & orSymbol --> logicalOperand
    and[/"LogicalAnd
x and y"/]
    or[/"LogicalOr
x or y"/]
    xor[/"LogicalXOr
x xor y"/]
    nandNor["LogicalNandNor
x nand y
x nor y"]
    andSymbol[/"LogicalAndSymbol
x && y"/]
    orSymbol[/"LogicalAndSymbol
x || y"/]


    %% logicalExpression ---> as
    logicalExpression --> and & or & xor & nandNor & andSymbol & orSymbol
    logicalExpression((" "))


    insideParens & assignPlain & assignArithmetic & assignShift & assignBitwise & assignLogical --> logicalExpression
    insideParens["Parenthesized
(…)"]
    assignPlain["Assignment
x = y"]
    assignArithmetic["Assignment
x += y
x -= y
x *= y
x /= y
x %= y"]
    assignShift["Assignment
x <<= y
x >>= y
x <<<= y
x >>>= y"]
    assignBitwise["Assignment
x &= y
x |= y
x ^= y"]
    assignLogical["Assignment
x &&= y
x ||= y"]

The graph above covers the partial ordering of all contemplated Unicode/Cilia operators. Relations that most developers can be expected to know are drawn as edges, e.g.

This avoids the well-known C/C++ pitfall where x & mask == 0 parses as x & (mask == 0); here it parses as the intended (x & mask) == 0.

Pairs that nobody reliably ranks are left unordered on purpose and therefore require explicit parentheses, e.g.:

The node shapes encode each group’s

i.e. what it means to chain the same precedence group without parentheses.
Circles are helper nodes only (not a precedence group).

%%{init: {'themeVariables': {'fontFamily': 'monospace'}}}%%
graph LR
    binary@{ shape: brace-r, label: "Binary" }

    nonAssociative["Non-Associative

a == b == c
needs parens"]

    leftToRightAssociative[/"Left-to-Right-Associative

a + b + c 
=
(a + b) + c"/]

    rightToLeftAssociative[\"Right-to-Left-Associative

a ** b ** c
=
a ** (b ** c)"\]


    unary@{ shape: brace-r, label: "Unary" }

    nonRepeating["Non-Repeating"]

    repeating{"Repeating

 x.y.z
 *&x
 T**"}


    helper@{ shape: brace-r, label: "Helper Node" }

    circle((" "))


    binary ~~~ nonAssociative ~~~ leftToRightAssociative ~~~ rightToLeftAssociative
    unary ~~~ nonRepeating ~~~ repeating
    helper ~~~ circle

Custom Operators with Declared Precedence

Note
Admittedly, all of this is rather complex.

For custom operator symbols, fixity and precedence must be declared explicitly. Operator precedence is resolved based on a partial ordering (roughly as in Swift SE-0077).

If two neighbouring infix operators have precedence groups without a defined relation, the expression requires parentheses. Otherwise it is a compile error, e.g. 1 + 2 & 3 is illegal.

Prefix and infix forms of the same symbol (e.g. -) are distinct registrations, as in C++ and Swift.

Declaration is in two separate steps:

1. Precedence Groups & Their Ordering

Named groups replace numeric precedence levels.

precedence Multiplication {
    associativity: left
    lowerThan: Power
}

precedence Power {
    associativity: right
    higherThan: Multiplication
}

2. Operator Registration & Implementation

Overload the registered symbol using the existing operator syntax.

operator (Set<T> a) ∖ (Set<T> b) -> Set<T> { ... }   // set difference: a without b
operator √(Float a) -> Float { ... }

Infix operators without a group belong to Default, prefix and postfix belong to the (high) precedence groups Prefix/Postfix.

Operator precedence groups can be declared explicitly:

operator (Set<T> a) ∪ (Set<T> b) -> Set<T> precedence Union { ... }          // union
operator (Set<T> a) ∩ (Set<T> b) -> Set<T> precedence Intersection { ... }   // intersection
operator (Matrix a) ⊗ (Matrix b) -> Matrix precedence Tensor { ... }         // tensor / Kronecker product

Compound-assignment variants remain member operators inside classes (operator +=(…), etc.).

Allowed operator characters

Operator names follow Swift’s operator-head + operator-characters grammar:

Word operators (and, or, nand, nor, xor, not) are standard-library built-ins; custom operators use symbol tokens.

TODO: Should users be able to declare custom word operators?

Bracket / “Sandwich” Operator

Bracket / “sandwich” operators (like ‖x‖, ⟨a, b⟩, …) are paired delimiters (“enclosing operator”, “delimited form”, “bracketed expression”, informally “sandwich operator”).

operator ‖Vec v‖ -> Float  { return v.length() }  // norm
operator ⟨T a, b⟩ -> Float { ... }                // inner product

More bracket variants (asymmetric pairs only; some may be used in reversed order, e.g. ≫...≪; see also Unicode Math Brackets):

Pair Category Name / note
⟨...⟩ angle angle brackets (inner product)
⟪...⟫ angle double angle brackets
⦑...⦒ angle angle bracket with dot
⦅...⦆ round double parenthesis
⟮...⟯ round flattened parenthesis
⦃...⦄ curly white curly bracket
⟦...⟧ square white / semantic (“Scott”) square brackets
⦋...⦌ square square bracket with underbar
⦍...⦎ square square bracket with ticks
⦏...⦐ square square bracket with ticks (mirrored)
⁅...⁆ square square bracket with quill
⌊...⌋ floor/ceiling floor (round down)
⌈...⌉ floor/ceiling ceiling (round up)
⦗...⦘ tortoise-shell black tortoise-shell bracket
⟬...⟭ tortoise-shell white tortoise-shell bracket
⦇...⦈ Z notation image bracket
⦉...⦊ Z notation binding bracket
⦓...⦔ arc arc less/greater-than bracket
⦕...⦖ arc double-line arc bracket
⟅...⟆ bag S-shaped bag delimiter
⌜...⌝ corners top corners (quine corners)
⌞...⌟ corners bottom corners
⸢...⸣ corners top half brackets
⸤...⸥ corners bottom half brackets
≪...≫ operator much-less/greater-than (relational operator, not a true bracket)
⋘...⋙ operator very-much-less/greater-than (operator)
‹...› quotation single guillemets (quotation, not math)
«...» quotation double guillemets (quotation, not math)
❨...❩ ornamental parenthesis ornament (decorative)
❪...❫ ornamental flattened parenthesis ornament
❬...❭ ornamental angle bracket ornament
❮...❯ ornamental heavy angle quotation ornament
❰...❱ ornamental heavy angle bracket ornament
❲...❳ ornamental tortoise-shell bracket ornament
❴...❵ ornamental curly bracket ornament

N-Ary Operators

N-ary operators need an index/binder (e.g. ∑_{i=1}^{n}) and are handled like functions or for-in-loops:

Later / Never

Many of the symbols seem more suitable for a computer algebra system (CAS) than for a general purpose programming language, so they stay unassigned for now.

Reserved for future use, as it could get complicated and confusing. Remaining candidate symbols, not yet assigned to one of the cases above (with their usual mathematical meaning):

OpenMP-like Parallel Programming

TODO
Syntactically this is not a good solution.

  • We avoid brackets in if and while, but then use it for reduce and schedule
  • Syntax should be better, clearer, or more powerful than plain OpenMP, otherwise better use just that.