Erlang Notes

Nim FFI
Login

Nim FFI

Working with Nim

The simplest way to have the BEAM machine reach out to some Nim shared objects and call some native functions, is to use (on Nim's side) the nimler library and (on Erlang's side) erlang:load_nif/2.

Note well:

{error,{reload,"NIF library already loaded (reload disallowed since OTP 20)."}}

Hot-reloading of NIF libraries has been gone for a long time (I'm on Erlang/OTP 26 now), so you'll need to cycle the node to load a changed Nim library. This also means that onUnload is useless for cleanup -- it's never called.

  1. hello world
  2. hello2 world - I/O without the debugEcho hack
  3. A Nim terminal frontend for Erlang

hello world

Given hello.nim:

import nimler

proc hello(env: ptr ErlNifEnv): ErlAtom {.xnif.} =
  debugEcho "hello from Nim!" # dodging noSideEffect
  AtomOk

exportNifs "hello", [ hello ]

And compiling that with

$ nim c --app:lib -d:release hello.nim

# or, to generate the Erlang listing below:
$ nim c --app:lib -d:nimlerGenWrapper -d:nimlerWrapperType=erlang -d:nimlerWrapperFilename=hello.erl -d:nimlerGenWrapperForce -d:release hello.nim

And hello.erl:

-module(hello).
-export([hello/0]).
-on_load(init/0).

init() -> ok = erlang:load_nif("./libhello", 0).

hello() -> exit(nif_library_not_loaded).

Then this function can be used like so:

1> c(hello).
{ok,hello}
2> hello:init().
ok
3> hello:hello().
hello from Nim!
               ok
4> 

Or with a oneliner:

$ erlc hello.erl  # if necessary
$ erl -noshell -s hello hello -s init stop
hello from Nim!

hello2 world - I/O without the debugEcho hack

The xnif pragma makes for a much easier interface, but it blocks use of the dirtyIo and dirtyCpu pragmas. Here's the low-level alternative:

import nimler

using
  env: ptr ErlNifEnv
  argc: cint
  argv: ptr UncheckedArray[ErlNifTerm]

proc hello(env, argc, argv): ErlNifTerm {.nif, arity: 0, dirtyIo.} =
  echo "hello2 from Nim!"
  return env.toTerm(ErlAtom("ok"))

exportNifs "hello2", [ hello ]

A Nim terminal frontend for Erlang




This uses a longer Nim file adapted from an illwill example.

Start the TUI example in one terminal:

$ make all tui

# alternately, with nimble:
$ nimble all
$ nimble tui

This will display some text with an animated background, and a central window showing the name of the last key that you pressed. This entire UI is running out of a Nim thread spawned by an Erlang node. You can leave this application running and connect to that Erlang node from another terminal:

$ erl -remsh keycodes

And you can control the terminal app from that shell:

Erlang/OTP 26 [erts-14.2.4] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]

Eshell V14.2.4 (press Ctrl+G to abort, type help(). for help)
(keycodes@bee)1> keycodes:setKey('H').
ok
(keycodes@bee)2> keycodes:setKey(not_an_illwill_key_name).
error
(keycodes@bee)3> keycodes:setKey(1234).
what
(keycodes@bee)4> keycodes:setKey('CtrlC').
ok
*** ERROR: Shell process terminated! (^G to start new job) ***