Skip to content

Proposal: Support inline fallback syntax for substitutions (e.g. ${VAR:default}) #827

@Frotas

Description

@Frotas

Description

Right now, HOCON lets you substitute variables and even mark them as optional. A common pattern looks like this:

port = ${?PORT}
port = 32

It works, but it feels a bit clunky. You end up writing two lines (or relying on layered configs) just to say “use this value, otherwise fall back to that one.” For people new to HOCON, the ordering rules can also be a little confusing.

Proposal

Add support for an inline fallback syntax, for example:

port = ${PORT:32}

Meaning:

  • If PORT is set → use its value.
  • If not → just use 32.

Motivation

  • Keeps configs shorter and easier to scan.
  • Matches what other systems already do (Spring Boot’s ${VAR:default}, for instance).
  • Saves you from repeating the same “optional + fallback” boilerplate across dozens of keys.

Considerations

  • Backward compatibility: today ${VAR:default} is just a literal string, so this would be a new behavior.
  • Parser ambiguity: the parser needs to clearly distinguish between a fallback and a literal :.
  • Consistency: ideally this should work for both environment variables and config keys.

Compatibility / Migration Plan

  • Existing configs that rely on ${VAR:literal} as a string would resolve differently.
  • Could be introduced behind a flag (e.g., ConfigParseOptions.enableInlineFallback(true)) or in a major version bump.
  • Tooling and linters may need updates to catch conflicts.

Grammar Changes

  • Extend substitution grammar so : inside ${} is treated as a fallback separator.
  • Only apply this rule when : is not inside quotes.
  • Nested substitutions should continue to work.

Implementation Notes

  • Requires changes in both the parser and substitution resolver.
  • Defaults should be type-safe: numbers, booleans, lists, and objects should all be valid, not just strings.

Testing / Edge Cases

  • Variables whose values actually contain :.
  • Nested fallbacks: ${VAR1:${VAR2:default}}.
  • Defaults with spaces or quoted strings.
  • Composite defaults, e.g.:
      list = ${LIST:[1,2,3]}
      map  = ${MAP:{a=1, b=2}}

Open Questions

  • Should defaults themselves allow interpolation (e.g., ${VAR:${OTHER:42}})?
  • Should whitespace around : be allowed?
  • Should this apply only to environment variables, or also to config keys and system properties?

Example

db.host = ${DB_HOST:localhost}
db.port = ${DB_PORT:5432}

If DB_HOST and DB_PORT are not set, the config resolves to:

db.host = "localhost"
db.port = 5432

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