There's another option and that's setting up an Erlang node in the other language. The Erlang term format is relatively straightforward. But I'm honestly not sure of the benefit of a node versus just using a port.
- can "easily" send beam terms back and forth
- if you want it to be os-supervised separately (systemd, kubernetes, e.g.)
- pain in the ass
Port:
- easy
- usually the only choice if you're not the software author
- really only communicates via stdio bytestreams
- risk of zombies if... Iirc the stdout is not closed properly?
- kind of crazy how it works, Erlang VM spawns a separate process as a middleman
It's not impossibly large but it's not something one does on a lark either; if there isn't support in your language already it's hard to justify this over any of the many, many message busses supported by both Erlang and other languages that don't have so many requirements.
We do have IPC handles that could enable this over, say, ports, but then there's a whole other discussion on pointers vs ipc handles
If you've got some mathematical/crypto function, chances are you don't want that to go through a command queue to an external port, because that's too much overhead. If it's a many round crypto function like bcrypt or something, you do need to be a bit careful doing it as a NIF because of runtime. But you wouldn't want to put a sha256 through an external program and have to pass all that data to it, etc.
Something that you might actually want queueing for and is likely to have potential for memory unsafety like say transcoding with ffmpeg, would be a good fit as an external Port rather than a NIF or a linked in Port driver.