blue matrix

Hup, design considerations

Right now, nothing has been said about:

  • What to do when multiple tables have the same name,
  • Error handling,
  • User-defined functions or operators,
  • Implementation details.

Multiple tables having the same name can be seen as a problem (which would lead to a namespace-based solution) or as a feature. I prefer to make it a feature. A table name represents a stack of values, potentially calculated by several formulas (if several tables have the same name). When a formula is recalculated, the old values are deleted from the stack and the new values are pushed onto it. So the user can always expect a table name to represent values in chronological order of update.

Since tables can generate and host other tables, table names can be qualified by path, using a filesystem metaphor. For example, parent/table would mean the table named table that's inside the table named parent.


I don't want to make error handling a cause of flow disruption. Instead, the system should be able to continue to work as best as it can, while isolating errors to prevent propagation, and having them be part of the current situation by logging them in dedicated error tables.

As a rule of thumb, when the evaluation of a formula produces an error, the result of that formula is merely an empty list. The built-in ERR table receives a formatted entry about the error. Errors can then be manually handled by the user.


User-defined functions are defined in tables. The name of the table is the signature of the function, and the unique value of the table is the body of the function.

User-defined functions always begin with a period "." character, and are followed by parentheses which receive the arguments, each preceded by a "#" character. The function name's first character is a lowercase letter.

[ .sqr() | # * # ]

[
    squared units
    = [units] .sqr()
    | 1
    | 4
    | 9
]

[ .pow(#power) | (#) & #power \* ]

[
  units to the third
    = [units] .pow(3)
    | 1
    | 8
    | 27
]

Remember that evaluation goes from left to right, everything being left-associative. In the body of a user-defined function, a single "#" character represents the values-so-far, or the "previous" operand. If the function was an infix operator, we could say that "#" represents its left-hand side operand.

You might be wondering why the "#" in the pow() function's body is between parentheses. That's because we want want to obtain (1 2 3) * (1 2 3) * (1 2 3) rather than 1 * 1 * 1 * 2 * 2 * 2 * 3 * 3 * 3.


I'm not yet sure of how all of this will be implemented. But the target language is Go (with Wails for the frontend), and goroutines are probably going to be used extensively.

Oh snap, I know how it will be implemented. By typing on a keyboard with my hands which are fortunately full of fingers.