Skip to content

Reading messages

Pony matches on types, not on values. A decoded message is a union of term classes, so a message is read by naming the types its parts must have. Nothing here casts, and nothing raises.

The shape

ErlangShapes.pair and ErlangShapes.triple hand back a tuple's leading elements, so one match binds each of them with its type. Both take the result of the decoder directly.

match ErlangShapes.pair(ErlangTermDecoder.decode(message))
| (let sender: ErlangPid, let text: ErlangBinary) =>
  _env.out.print(text.value + " from " + sender.string())
else
  _env.out.print("not a {Pid, Binary}")
end

A term of another arity, another kind, or no term at all falls to else.

The values

ErlangValues takes a payload out of a term. Each function answers None for a term of another kind and for no term, so a lookup and the read that follows stay one expression with two clauses.

let timeout: I64 =
  match ErlangValues.integer(config.get_binary("timeout"))
  | let found: I64 => found
  | None => 5_000   // no such key, or a value of another kind
  end

The same functions: atom_name, bytes, chars, integer, float, pid.

The keys

A binary and an atom of one name are two different keys, so a lookup says which kind it means.

let config = ErlangMap.binary_keyed([("timeout", ErlangInteger(5_000))])

config.get_binary("timeout")   // ErlangInteger(5000)
config.get_atom("timeout")     // None: that is another key

ErlangMap.atom_keyed builds the other kind, get takes a term as the key, and ErlangTuple.at and ErlangList.at read a position.

Decode once, then keep no terms

A handler is easier to read when it never sees a term. Decode into a value of your own in one function:

use "erl_interface_pony"

class val Call
  let sender: ErlangPid
  let text: String

  new val create(sender': ErlangPid, text': String) =>
    sender = sender'
    text = text'

primitive CallDecoder
  fun apply(message: EMessage ref): (Call | None) =>
    match ErlangShapes.pair(ErlangTermDecoder.decode(message))
    | (let sender: ErlangPid, let text: ErlangBinary) =>
      Call(sender, text.value)
    else None
    end
match CallDecoder(message)
| let call: Call => _env.out.print("asking: " + call.text)
else _env.out.print("not a {Pid, Binary}")
end

One more thing worth knowing: ErlangTerms.eq(term, ErlangAtom("reply")) and ErlangTerms.is_atom(term, "reply") compare a tag, which a type pattern cannot do.