Skip to content

Docs: clarify CONCAT_WS behavior with absent/partial input values #3

Description

@mitselek

Summary

The Formulas docs describe CONCAT_WS as "Joins values with a separator — the last value is used as the separator" and add that "When no values resolve... most functions return undefined." But the partial-resolution case — when some inputs are absent and others present — isn't documented. This case is the most useful one in practice (it's how you express coalesce/fallback patterns that the formula DSL otherwise can't), and the actual behavior is better than the docs suggest. Worth spelling out so users can confidently rely on it.

What I tested

Created a temporary entity type with three properties:

  • a — string
  • b — string
  • combined — formula: a b ' / ' CONCAT_WS

Then four instances with different combinations of a and b. Read back the formula's stored value.

a b combined
"A" "B" "A / B"
"A" absent "A"
absent "B" "B"
absent absent "" (empty string)

Useful findings (please document)

  1. Absent values are skipped cleanly. When one input is present and another absent, CONCAT_WS returns only the present value — no stray separator. This makes it a usable fallback / coalesce pattern. Example: value_a value_b ' / ' CONCAT_WS returns value_a when value_b is absent.
  2. Both-absent case returns empty string, not undefined. Slightly different from the "most functions return undefined" statement in the docs. The combined property gets written with value "" in this case (the property exists, with empty string value). Whether that's intentional or a minor inconsistency with MIN/MAX/AVERAGE is unclear — but it's worth being explicit either way.

Suggested docs change

Under Formulas → Empty Input Behaviour, add a row for CONCAT_WS and clarify the partial-resolution case:

| Function   | Empty-input result | Partial-input behaviour |
|------------|--------------------|--------------------------|
| CONCAT     | undefined           | concatenates resolved values |
| CONCAT_WS  | "" (empty string)   | joins resolved values, skips absent ones (no stray separators) |

Or add a dedicated "Coalesce / fallback patterns" subsection showing the idiom — it's a non-obvious but very useful capability.

Why this matters in practice

We're using this for property inheritance in Polyphony's Entu schema — editions can override their parent work's voicing / duration / language, with a *_display formula that shows either the edition's own value (if set) or the work's value (if not), or both side-by-side when they diverge:

voicing _parent.work.original_voicing ' / ' CONCAT_WS

This wouldn't be safe to rely on without the empirical confirmation above — the docs leave the behavior ambiguous enough that I had to live-probe before committing to it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions