Erlang term conversion

Erlang/Elixir terms are represented in nimler with the opaque type ErlNifTerm. Nimler exposes functions for converting between nim types and Erlang terms.

fromTerm()

fromTerm() produces nim type from ErlNifTerm. fromTerm() returns an Option.

let i_option = env.fromTerm(term, int32)

if i_option.isNone():
  # The term was not successfully read into an int32
else:
  let i = i_option.get()

# Default value of 0 if term is not read successfully
let ii = env.fromTerm(term, int32).get(0)

toTerm()

toTerm() produces ErlNifTerm from nim type.

let term = env.toTerm(10)
let other_term = env.toTerm(10'i32)

Supported Types

ErlangElixirNimler
IntegerIntegerint, int32, int64, uint, uint32, uint64
FloatFloatfloat
AtomAtomdistinct string
AtomAtombool
StringCharlistseq[char]
BitstringStringstring
BinaryBinaryseq[byte]
ListListseq[T]
TupleTupletuple
MapMapTable[A, B]
PIDPIDErlPid
TermTermErlTerm

Atoms

let term = env.toTerm(ErlAtom("test"))
# :test

let atom = env.fromTerm(term, ErlAtom).get()
# ErlAtom("test")

Note: The following atom constants are exported from nimler. Note that these are not yet converted to ErlNifTerm:

  • AtomOk = ErlAtom("ok")
  • AtomError = ErlAtom("error")
  • AtomTrue = ErlAtom("true")
  • AtomFalse = ErlAtom("false")

Booleans

let term = env.toTerm(true)
# :true

let atom = env.fromTerm(term, bool).get()
# true

Charlists

let term = env.toTerm(@"test")
# 'test'

let lst = env.fromTerm(term, seq[char]).get()
# @['t', 'e', 's', 't']

Strings

Strings follow the Elixir pattern of using Erlang bitstring rather than charlists

let term = env.toTerm("test")
# "test"

let str = env.fromTerm(term, string).get()
# "test"

Binaries

let term = env.toTerm(toOpenArrayByte("test", 0, 3))
# <<116, 101, 115, 116>>

let bin = env.fromTerm(term, seq[byte]).get()
# @[116, 101, 115, 116]

Lists

Elements of seq must be of the same type.

let term = env.toTerm(@[1,2,3])
# [1,2,3]

let lst = env.fromTerm(term, seq[int]).get()
# @[1,2,3]

Tuples

Tuples in nim may contain mixed types.

let term = env.toTerm(("test",1,3.14))
# {"test",1,3.14}

let (a,b,c) = env.fromTerm(term, (string, int, float)).get()
# a="test"
# b=1
# c=3.14

Keyword lists

Keyword lists (lists of {Atom, }) are represented with the type ErlKeywords. Passing generic object types to fromTerm() or toTerm() also implies keyword list.

type MyObj = object
  a: int
  b: float
  c: string

let term = toTerm(env, MyObj(a: 1, b: 1.1, c: "test"))
# keyword list: [a: 1, b: 1.1, c: <<"test">>]

let o = fromTerm(env, term, MyObj).get()
# MyObj{a,b,c}

Maps

Maps are represented in nimler with the Table[K, V] type.

import tables

var t = initTable[string, int](4)
t["a"] = 1
t["b"] = 2

let term = env.toTerm(t)
# %{"a" => 1, "b" => 2}

let tt = env.fromTerm(term, Table[string, int]).get()
# {"a": 1, "b": 2}

Opaque terms

Sometimes specifying opaque ErlTerm type is convenient. Example with the positional nimler API:

func addThing(env: ptr ErlNifEnv, myArg: ErlTerm): ErlTerm {.xnif.} =
  # fromTerm(env, myArg, int).get()
  # fromTerm(env, myArg, string).get()
  # fromTerm(env, myArg, seq[byte]).get()

PIDs

Process IDs are represented in nimler with the ErlPid type.

let pid = fromTerm(env, term, ErlPid).get()

Results

Result tuples have a first element either :ok or :error. Nimler functions env.ok() and env.error() accept varargs.

let ok_term = env.ok(env.toTerm(1), env.toTerm(2))
# {:ok, 1, 2}

let err_term = env.error(env.toTerm("Bad thing"))
# {:error, "Bad thing"}