Identifiers#

An identifier is a string used to refer to variables, built-in constants, built-in functions, parameters, and other named entities.

$ xa '
  variable := 10
  variable
'
# 10

$ xa 'MATH.PI'
# 3.141592653589793

$ xa 'SQRT(4)'
# 2.0

$ xa '
  function := x, y, z -> x + y + z
  function(100; 20; 3)
'
# 123

Characters Allowed in Identifiers#

The following characters can be used in identifiers:

  • Letters a-zA-Z
  • Digits 0-9
  • Underscore _
  • All multibyte characters

However, the first character must not be a digit.

Quoted Identifiers#

Identifiers enclosed in ` ` are called quoted identifiers.

Identifiers and quoted identifiers represent the same identifier, just written differently.

$ xa '
  `variable` := 10
  `variable`
'
# 10

$ xa '
  variable := 10
  `variable`
'
# 10

Quoted Identifiers with Symbols#

Quoted identifiers can also include symbols.

$ xa '
  `#` := 10
  `#`
'
# 10

Character Escapes in Quoted Identifiers#

The following character escapes can be used within quoted identifiers:

String Meaning
\xXX 1 character at specified code point U+0000~U+00FF
\uXXXX 1 UTF-16 code unit at specified position
$ xa '
  `\x21` := 10
  `!`
'
# 10

Assignment Statements#

Compound Assignment Operators#

Xarpite has compound assignment operators such as variable += value that perform both operation and assignment simultaneously.

In principle, variable += value works equivalently to variable = variable + value.

$ xa -q '
  x := 100
  OUT << x
  x += 23
  OUT << x
'
# 100
# 123

The following is a list of compound assignment operators available in Xarpite.

Operator Notation Override Method
Addition assignment operator variable += value _+=_
Subtraction assignment operator variable -= value _-=_

Overriding Compound Assignment Operators#

If the left-hand side of a compound assignment operator implements an override method, that method is called instead of the normal compound assignment.


Each override method takes the left-hand object and the right-hand value, as well as an accessor function that performs get and set operations on the expression, as arguments.

Calling accessor with 0 arguments performs a value get operation on the expression.

Calling accessor with 1 argument performs a value set operation on the expression.

Compound assignment operator behavior can be defined either as an object mutation operation or as an immutable operation with assignment.


The return value of a compound assignment operator is the return value of the override method.


The following example defines it as an object mutation operation.

Two instances of a Fruits object with an overridden += operator are created, and the += operator is applied to both in a chained manner.

$ xa -q '
  Fruits := {
    new := () -> Fruits{array: []}
    `_+=_`: this, fruit, accessor -> (
      this.array += fruit
      fruit
    )
  }

  fruits1 := Fruits.new()
  fruits2 := Fruits.new()

  OUT << "fruits1: $(fruits1.array)"
  OUT << "fruits2: $(fruits2.array)"
  fruits1 += fruits2 += "apple"
  OUT << "fruits1: $(fruits1.array)"
  OUT << "fruits2: $(fruits2.array)"
'
# fruits1: []
# fruits2: []
# fruits1: [apple]
# fruits2: [apple]

The following example defines it as an assignment operation.

You can see that performing a compound assignment on fruits1 does not affect fruits2, which is a different variable.

$ xa -q '
  Fruits := {
    new := array -> Fruits{array: array}
    `_+=_`: this, fruit, accessor -> (
      accessor(new(this.array + [fruit]))
      fruit
    )
  }

  fruits1 := Fruits.new([])
  fruits2 := fruits1

  OUT << "fruits1: $(fruits1.array)"
  OUT << "fruits2: $(fruits2.array)"
  fruits1 += "apple"
  OUT << "fruits1: $(fruits1.array)"
  OUT << "fruits2: $(fruits2.array)"
'
# fruits1: []
# fruits2: []
# fruits1: [apple]
# fruits2: []

Compound Assignment on Non-Assignable Expressions#

When an expression doesn’t support assignment, only the override method check is performed.

The accessor supports get operations only.

Typically, this behavior is defined as a mutation operation on mutable values.

$ xa '
  Fruits := {
    `_+=_`: this, fruit, accessor -> (
      this.array += fruit
      this.array
    )
  }

  Fruits{array: []} += "apple"
'
# [apple]

Increment/Decrement#

Increment formula++ and decrement formula-- are operators that add or subtract 1 to/from an expression and assign the result.

$ xa '
  a := 10
  a++
  a
'
# 11

$ xa '
  a := 10
  a--
  a
'
# 9

Increment and decrement operations are equivalent to evaluating the expression, adding or subtracting 1, and assigning the result back to the original expression.

Postfix and Prefix Versions#

In addition to the postfix version, increment/decrement operators also have prefix versions ++formula and --formula.

The postfix version returns the value before addition/subtraction, while the prefix version returns the value after addition/subtraction.

$ xa '
  a := 10
  a++
'
# 10

$ xa '
  a := 10
  ++a
'
# 11

From a readability perspective, the postfix version is more commonly used.

Postfix-of-Prefix Increment/Decrement#

The “prefix” increment/decrement operators have “postfix-of-prefix” versions formula.++ and formula.--.

Like other postfix versions of prefix operators, these are syntactic sugar for writing prefix operators in postfix notation.

That is, their behavior is equivalent to the “prefix” version, returning the value after addition/subtraction.

$ xa '
  a := 10
  a.++
'
# 11

Incrementing Non-Numeric Values#

The behavior of formula++ is roughly equivalent to the following pseudocode:

old = get(formula)
new = plus(old, 1)
set(formula, new)
return old

Actually, as long as formula supports assignment and addition/subtraction with the integer 1 is defined, increment/decrement can work with non-numeric types as well.

$ xa '
  s := "abc"
  s++
  s
'
# abc1

Overriding Increment/Decrement#

Increment and decrement operators can be overridden with dedicated methods.

Operator Method Name Description
formula++ _++ Postfix increment
++formula ++_ Prefix increment
formula-- _-- Postfix decrement
--formula --_ Prefix decrement

By overriding these methods, you can customize the behavior of increment and decrement operations.


Override methods take an accessor function as an argument in addition to the object itself. This function performs get and set operations on the expression.

Calling accessor with 0 arguments performs a value get operation on the expression.

Calling accessor with 1 argument performs a value set operation on the expression.

Increment and decrement behavior can be defined either as an object mutation operation or as an immutable operation with assignment.

$ xa -q '
  MutableCounter := {
    new := value -> MutableCounter{value: value}
    `_++`: this, accessor -> (
      this.value++
    )
    `&_`: this -> this.value.&
  }

  old := MutableCounter.new(0)
  new := old

  OUT << "Old: $old"
  OUT << "New: $new"
  new++
  OUT << "Old: $old"
  OUT << "New: $new"
'
# Old: 0
# New: 0
# Old: 1
# New: 1
$ xa -q '
  ImmutableCounter := {
    new := value -> ImmutableCounter{value: value}
    `_++`: this, accessor -> (
      accessor(new(this.value + 1))
    )
    `&_`: this -> this.value.&
  }

  old := ImmutableCounter.new(0)
  new := old

  OUT << "Old: $old"
  OUT << "New: $new"
  new++
  OUT << "Old: $old"
  OUT << "New: $new"
'
# Old: 0
# New: 0
# Old: 0
# New: 1

The operator’s return value is the return value of the override method.

Due to this property, prefix and postfix versions are defined as independent operations, and one does not fall back to the other.

$ xa -q '
  Object := {
    `_++`: this, accessor -> "suffix"
    `++_`: this, accessor -> "prefix"
  }

  object := Object{}

  OUT << object++
  OUT << ++object
'
# suffix
# prefix

Increment/Decrement on Non-Assignable Expressions#

When an expression doesn’t support assignment, only the override method check is performed.

The accessor supports get operations only.

Typically, this behavior is defined as a mutation operation on mutable values.

$ xa '
  MutableCounter := {
    `_++`: this, accessor -> (
      this.value++
      this.value
    )
  }
  MutableCounter{value: 100}++
'
# 101

Variables#

Variables are a mechanism for storing and referencing values by naming them with identifiers.

$ xa '
  x := 100
  y :=  20
  z :=   3

  x + y + z
'
# 123

Variable Declaration#

To declare a variable, use the variable declaration operator variable := value.

The variable declaration operator declares a variable within the written scope while initializing it with the right-hand value.

In Xarpite, variables must be initialized with some value at the time of declaration.

$ xa '
  x := 10
  x
'
# 10

Assigning Values to Variables#

To assign a value to a variable, use the assignment operator variable = value.

The destination variable must already be declared.

$ xa -q '

  x := 10
  OUT << x

  x = 123
  OUT << x

'
# 10
# 123

Variable Scope#

Declared variables are valid only within their scope from the point of declaration onward.

Variables declared within operators that create a scope, such as ( ), are destroyed when that scope is exited.

$ xa -q '

  x := "A (outer initial value)"
  OUT << x

  (
    x = "B (outer assigned value)"
    OUT << x

    x := "C (inner initial value)"
    OUT << x

    x = "D (inner assigned value)"
    OUT << x
  )

  OUT << x
'
# A (outer initial value)
# B (outer assigned value)
# C (inner initial value)
# D (inner assigned value)
# B (outer assigned value)

Self-Reference from the Right-Hand Side of Variable Declaration Operator#

On the right-hand side of the variable declaration operator, you can reference the variable itself.

This property is convenient when creating recursive functions.

$ xa '
  factorial := n -> n == 0 ? 1 : n * factorial(n - 1)
  factorial(5)
'
# 120

Mount#

Mount is a mechanism that allows identifiers without declared variables to reference values from a special table.

Mount is often used when utilizing libraries.

Built-in constants such as TRUE NULL and built-in functions such as JOIN SQRT are all provided using the mount mechanism.

$ xa '
  lib := {
    fruit: "apple"
  }

  @lib

  fruit
'
# apple

Mount Operator#

The mount operator @object mounts the contents of object object at the current location.

When attempting to access an undefined variable, it searches for the value from mounted entries.

$ xa '
  @{
    fruit: "apple"
  }

  fruit
'
# apple

object must be an object.

Mount Operator Mounts the Contents of an Object#

Mount is performed on each entry of the object, not the object itself.

Modifications to the object made after mounting are not reflected in the mount state.

$ xa '
  lib := {
    fruit: "apple"
  }

  @lib

  lib.fruit = "orange"

  fruit
'
# apple

For the same reason, method calls using mount are also not possible.

Mounts Can Be Multiple#

When mounting is performed again in a state where mounting has already been performed, both entries are in a mounted state.

$ xa '
  @{
    fruit: "apple"
  }

  @{
    drink: "coffee"
  }

  "fruit=$fruit, drink=$drink"
'
# fruit=apple, drink=coffee

Mount Overwrites Existing Mounts#

When multiple mounts are performed on the same name, later ones take precedence.

$ xa '
  @{
    fruit: "apple"
    bread: "epi"
  }

  @{
    vegetable: "tomato"
    fruit: "orange"
  }

  "fruit=$fruit, bread=$bread, vegetable=$vegetable"
'
# fruit=orange, bread=epi, vegetable=tomato

Mount Has the Same Scope as Variables#

Mount is released when exiting the scope.

The scope of a mount is the same as variables, from when it is mounted by the mount operator until exiting the parenthetical expression at that level.

$ xa -q '
  @{
    fruit: "apple"
  }

  (
    OUT << fruit

    @{
      fruit: "banana"
    }

    OUT << fruit
  )

  OUT << fruit
'
# apple
# banana
# apple

Declared Variables Take Precedence Over Mounts#

When both a variable and a mount are accessible with the same name, the variable always takes precedence regardless of declaration order.

$ xa '
  fruit := "apple"

  @{
    fruit: "banana"
  }

  fruit
'
# apple

This specification is provided to prevent unintended use of mounts.

Delegated Variables#

When you declare a variable with a prefix \ like \variable, it becomes a delegated variable.

Delegated variables execute the assigned function instead of accessing the variable entity when getting or setting.


On get, the receiver function is called with 0 arguments.

$ xa -q '
  time := 0
  \now := () -> time

  time = 100
  OUT << now
  time = 200
  OUT << now
'
# 100
# 200

On set, the receiver function is called exactly once with 1 argument.

Since the assignment operator returns the right-hand value, the receiver function’s return value is always ignored.

If the receiver function returns a stream, the stream is resolved and the result is ignored.

$ xa -q '
  time := 0
  \now := _ -> time = _

  now = 100
  OUT << time
  now = 110
  OUT << time
'
# 100
# 110

Delegated Variables Supporting Both Get and Set#

To create a delegated variable that supports both getting and setting, the delegated function determines the number of arguments.

$ xa -q '
  time := 0
  \now := _ -> __.$# == 0 ? time : (time = _)

  now = 100
  OUT << [time, now] >> CSV
  now = 110
  OUT << [time, now] >> CSV
'
# 100,100
# 110,110