How do conversion and introspection operators work in EK9?

← Operators and Expressions · Ref: Q242

EK9 provides conversion and introspection operators that extract information from objects. All are pure, take 0 arguments, and return strictly enforced types.

STRING CONVERSION ($)

The $ operator returns a String representation. It is called implicitly in string interpolation.

  operator $ as pure
    <- rtn as String: ...

When you write `${myObj}`, the compiler calls the $ operator.

JSON CONVERSION ($$)

The $$ operator returns a JSON representation as a String. The JSON type has this built-in.

  operator $$ as pure
    <- rtn as String: ...

HASHCODE (#?)

The #? operator returns an Integer hash code for use in Dict keys and equality checking.

  operator #? as pure
    <- rtn as Integer: ...

ISSET (?)

The ? operator returns Boolean indicating whether the object has a meaningful, usable value.

  operator ? as pure
    <- rtn as Boolean: ...

This is central to EK9's tri-state semantics (absent, present-unset, present-set).

PROMOTE (#^)

The #^ operator returns a DIFFERENT type (type promotion). It MUST return a type different from self. The compiler uses this for automatic type widening.

  operator #^ as pure
    <- rtn as Float: ...

PREFIX AND SUFFIX (#< and #>)
The #< operator extracts a prefix, the #> operator extracts a suffix. On String, #< returns the first character and #> returns the last character.

  first <- #< fullName
  last <- #> fullName

EMPTY AND LENGTH

The 'empty' operator returns Boolean (true if the object is empty). The 'length' operator returns Integer (size/count).

  if empty collection
    stdout.println("Nothing here")
  size <- length myList

CLOSE

The 'close' operator performs resource cleanup. It is pure and returns nothing.

See Q25 for the promote operator in detail. See Q96 for class operators. See Q116 for default operator generation. See Q238 for the complete operator set. See Q245 for a complete custom type example.

Example

defines module qa.operators.conversion

  defines class

    Money
      amount <- Float()
      currency <- String()

      Money() as pure
        ->
          amount as Float
          currency as String
        this.amount :=: amount
        this.currency :=: currency

      operator $ as pure
        <- rtn as String: `${currency} ${amount}`

      operator #? as pure
        <- rtn as Integer: #?amount + #?currency

      override operator ? as pure
        <- rtn as Boolean: amount? and currency?

      operator #^ as pure
        <- rtn as Float: Float(amount)

      operator <=> as pure
        -> other as Money
        <- rtn as Integer: amount <=> other.amount

      operator == as pure
        -> other as Money
        <- rtn as Boolean: amount == other.amount and currency == other.currency

  defines program

    ConversionDemo()
      stdout <- Stdout()

      price <- Money(19.99, "USD")

      // === STRING CONVERSION $ (implicit in interpolation) ===

      stdout.println(`Price: ${price}`)

      // === ISSET ? ===

      stdout.println(`Is set: ${price?}`)

      // === HASHCODE #? ===

      hash <- #? price
      stdout.println(`Hash: ${hash}`)

      // === PROMOTE #^ ===

      floatValue as Float: price
      stdout.println(`Promoted to Float: ${floatValue}`)

      // === PREFIX AND SUFFIX ON STRING ===

      name <- "Hello"
      first <- #< name
      last <- #> name
      stdout.println(`Prefix: ${first}`)
      stdout.println(`Suffix: ${last}`)

      // === LENGTH ===

      text <- "Hello World"
      size <- length text
      stdout.println(`Length: ${size}`)

Common mistakes

E50060 — String has no size() method. Use the length prefix operator or .length() method for string length. See ek9 -h E50060 for details.

Incorrect:

size <- text.size()

Correct:

size <- length text

E07500 — Conversion operators like $, #?, and ? must be marked 'as pure' because they extract information without side effects. Omitting 'as pure' triggers E07500. See ek9 -h E07500 for details.

Incorrect:

operator $

Correct:

operator $ as pure

E07550 — The #? (hashcode) operator must return Integer. Returning String instead triggers E07550. See ek9 -h E07550 for details.

Incorrect:

<- rtn as String: #?amount + #?currency

Correct:

<- rtn as Integer: #?amount + #?currency
Other ways to ask this
  • How does string interpolation use the $ operator in EK9?
  • What do the prefix and suffix operators do in EK9?
  • How do I convert my type to String or JSON in EK9?

Coming from another language?

Java: toString() for String, no built-in JSON, hashCode() for hash, equals() check not isSet, no prefix/suffix operators. Python: __str__() and __repr__() for String, json.dumps() for JSON, __hash__() for hash, __bool__() for truthiness. Rust: Display trait for String, serde for JSON, Hash trait for hash, no isSet concept. Go: String() method by convention, json.Marshal() for JSON, no hash interface. Kotlin: toString() for String, no built-in JSON operator, hashCode() for hash. JavaScript: toString() for String, JSON.stringify() for JSON, no custom hash. EK9: $ returns String, $$ returns JSON, #? returns Integer hashcode, ? returns Boolean isSet, #^ returns promoted type, #< prefix, #> suffix, empty returns Boolean, length returns Integer.

Keywords: operator, interpolation, length, backtick, json, empty, string, prefix, suffix, conversion, expression, isset, close, introspection, hashcode, promote