How do I provide a default value when something might be unset in EK9?

← Control Flow · Ref: Q985

EK9 provides two operators for default values:

?: COALESCE (ELVIS)

Returns the left value if SET, otherwise the right:

  displayName <- userName ?: "Guest"
  timeout <- configuredTimeout ?: defaultTimeout

:=? ASSIGN-IF-UNSET

Only assigns if the target is currently UNSET:

  serverHost <- String()
  serverHost :=? "localhost"
  serverHost :=? "other"     has no effect — already set to localhost

WHEN TO USE EACH

?: when creating a new variable with a fallback.
:=? when conditionally initialising an existing variable.

COMBINING WITH GUARDS

  if connection <- tryConnect(primaryHost)
    useConnection(connection)
  else
    fallbackHost <- primaryHost ?: backupHost
    if backup <- tryConnect(fallbackHost)
      useConnection(backup)

NOT ?? FOR DEFAULTS

?? (null coalescing) only tests whether the left operand is ABSENT. An unset value such as String() is present, so ?? passes it straight through and the result is still unset:

  name <- String()
  name ?? "Guest"       still unset
  name ?: "Guest"       "Guest"

See Q1059 for the difference between ?? and ?:.

Example

defines module qa.controlflow.defaultvalue

  defines function

    lookupSetting() as pure
      -> settingName as String
      <- rtn as String: String()

      if settingName == "host"
        rtn: "production.example.com"

  defines program

    DefaultValueDemo()
      stdout <- Stdout()

      // ?: for inline defaults
      configuredHost <- lookupSetting("host")
      activeHost <- configuredHost ?: "localhost"
      stdout.println(`Host: ${activeHost}`)

      // ?: when lookup returns unset (?? would pass the unset value through)
      missingPort <- lookupSetting("port")
      activePort <- missingPort ?: "8080"
      stdout.println(`Port: ${activePort}`)

      // :=? for conditional initialisation
      logLevel <- String()
      logLevel :=? "INFO"
      stdout.println(`Log level: ${logLevel}`)

      // :=? has no effect when already set
      logLevel :=? "DEBUG"
      stdout.println(`Still: ${logLevel}`)
Other ways to ask this
  • How do I fall back to a default in EK9?
  • What is the EK9 equivalent of a default value expression?
  • Does ?? or ?: give an unset value a default in EK9?
  • How do I use :=? for conditional initialisation?

Coming from another language?

EK9 uses ?: (elvis) for an inline default and :=? for assign-if-unset. Both handle unset values without any concept of null. Unlike C#/Swift/JavaScript, EK9's ?? does NOT default an unset value - it only replaces an absent one.

Keywords: guarded, coalescing, fallback, default, unset