{"id":23459588,"url":"https://github.com/d-led/otp_pony_node","last_synced_at":"2025-04-14T04:15:12.584Z","repository":{"id":44902191,"uuid":"164326870","full_name":"d-led/otp_pony_node","owner":"d-led","description":"An Erlang C Node for the Pony language via ei_connect","archived":false,"fork":false,"pushed_at":"2024-01-11T19:17:22.000Z","size":3073,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-14T04:15:06.130Z","etag":null,"topics":["actor-model","beam","c-node","erlang","otp","pony-language"],"latest_commit_sha":null,"homepage":"","language":"Pony","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/d-led.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-06T16:51:56.000Z","updated_at":"2024-06-04T21:58:13.000Z","dependencies_parsed_at":"2022-09-16T03:41:36.503Z","dependency_job_id":null,"html_url":"https://github.com/d-led/otp_pony_node","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-led%2Fotp_pony_node","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-led%2Fotp_pony_node/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-led%2Fotp_pony_node/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-led%2Fotp_pony_node/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/d-led","download_url":"https://codeload.github.com/d-led/otp_pony_node/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248819408,"owners_count":21166477,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actor-model","beam","c-node","erlang","otp","pony-language"],"created_at":"2024-12-24T06:16:01.730Z","updated_at":"2025-04-14T04:15:12.565Z","avatar_url":"https://github.com/d-led.png","language":"Pony","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Erlang C Node for the Pony Language (spike/WIP)\n\n[![Docker Image CI](https://github.com/d-led/otp_pony_node/actions/workflows/docker-image.yml/badge.svg)](https://github.com/d-led/otp_pony_node/actions/workflows/docker-image.yml)\n\n[![Build Status](https://travis-ci.org/d-led/otp_pony_node.svg?branch=master)](https://travis-ci.org/d-led/otp_pony_node) (old)\n\n## Motivation\n\nWhile different [Actor Model](https://www.brianstorti.com/the-actor-model/) implementations may differ in many details,\ntransferring the knowledge and design considerations between them is not too hard. An Actor is a unit of concurrency,\nand processes its messages synchronously. A run-time that includes a scheduler and some form of mailboxes\nfor the Actors makes sure, the CPU is utilized as desired (which may vary from implementation to implementation).\nConcurrent and distributed software \nnot written with the Actor Model implementation [needs to solve problems]( http://rvirding.blogspot.com/2008/01/virdings-first-rule-of-programming.html), such as safe distribution and scheduling of work onto CPUs/cores,\ngranularity of the scheduled computations, work interruption, resource clean-up, fault-tolerance.\n\nPony and the BEAM (Erlang/Elixir/others) have different design goals and give different guarantees.\nIn a project, where the benefits of both need to be utilized, it might be beneficial to simply partition the problem,\nand solve each problem with a dedicated Actor Model implementation. Depending on the use-case and the boundary conditions,\na different communication channel between the parts of the application can be chosen. This project attempts to provide\nan option to write [Erlang C Nodes]( http://erlang.org/doc/man/ei_connect.html) in Pony to exchange messages between\nthe two run-times the Erlang way. There are other options, of course,\ne.g. via ZeroMQ or any other appropriate transport available to both technologies.\n\nA particular sweet spot for Pony is its [built-in FFI](https://tutorial.ponylang.io/c-ffi.html) that doesn’t require\nan extra build system or config, given a shared library can be found. The BEAM has another sweet-spot,\nas it can isolate the failures, timeouts and deadlocks of native code [by means]( http://erlang.org/doc/reference_manual/ports.html)\nof starting native code in another OS process and treating a handle to it as a process (Actor).\nGiven a Pony C Node process, connected to a parent Erlang process, utilizing existing native libraries can be simplified without giving up the Actor Model.\n\n\n## POC\n\n- build: OSX, Linux: `./build.sh`, Windows: `build.bat`\n- demo: `./test.sh`\n\nsuccessful POC:\n\n- messages received in Pony\n- messages sent from Pony\n- graceful handling of a failed receive\n- parsing the messages (in progress)\n- encoding new messages (in progress)\n\n```txt\n$ ./otp_pony_node\nConnection successful\n1: ERL_SMALL_TUPLE 2bytes\n3: ERL_PID 0bytes\n29: ERL_BINARY 6bytes\nReceived: 100bytes\npid: demo@localhost\natom: 7: Hi!\n1: ERL_SMALL_TUPLE 2bytes\n3: ERL_PID 0bytes\nReceived: 100bytes\npid: demo@localhost\n29: ERL_BINARY 6bytes\natom: 6: Hi!\nReceive failed. Disconnecting\n```\n\nwindows (release mode, messages sent from iex):\n\n```txt\nD:\\src\\otp_pony_node\u003eotp_pony_node.exe\nConnection successful\nReceived: 100bytes\npid: demo@localhost\natom: 0: Hi!\nReceived: 100bytes\npid: demo@localhost\natom: 1: Hi!\nReceive failed. Disconnecting\n```\n\n## Sending messages to the Pony node from the IEx\n\n```elixir\n$ iex --sname demo@localhost --cookie secretcookie\niex(demo@localhost)1\u003e {:ok, hostname} = :inet.gethostname\n{:ok, '...'}\niex(demo@localhost)2\u003e pony = {:any, :\"pony@#{String.downcase(\"#{hostname}\")}\"}\n{:any, :\"pony@...\"}\niex(demo@localhost)3\u003e send(pony, {self(),\"0: Hi!\"})\n{#PID\u003c0.109.0\u003e, \"0: Hi!\"}\n```\n\n## Current API Preview\n\n```pony\n// connecting\nlet erl = EInterface(\"pony\", \"secretcookie\")\nmatch erl.connect(\"demo@localhost\")\n| ConnectionFailed =\u003e \n    _env.out.print(\"Connection failed. Exiting\")\n    return\n| ConnectionSucceeded =\u003e\n    _env.out.print(\"Connection successful\")\nend\n\n// receiving a message\nmatch erl.receive_with_timeout(5_000/*ms*/)\n| ReceiveFailed =\u003e\n    _env.out.print(\"Receive failed. Disconnecting\")\n    erl.disconnect()\n    return\n| ReceiveTimedOut =\u003e\n    _env.out.print(\"Receive timed out. Disconnecting\")\n    erl.disconnect()\n    return\n| let m: EMessage =\u003e\n    handle_message(m)\nend\n\n// handle_message: parsing the message linearly\n(var arity, var pos) = m.tuple_arity_at(m.beginning)\nif arity != 2 then\n    _env.out.print(\"Didn't expect tuple arity of \" + arity.string())\n    return\nend\n\n// print the term type of the token at pos\nm.debug_type_at(pos)\n\n(var pid, pos) = m.pid_at(pos)\n// do something with pid ...\n\n// pos is mutable and gets updated after each successful token parsed\n(let msg, pos) = m.binary_at(pos)\n// do something with msg\n\n// sending a message\nlet pid2 = ErlangPid.create(\"demo@localhost\", 97, 0, 3) /* or the received one */\nlet m = EMessage.begin()\nm.encode_atom(\"hello from Pony!\")\nerl.send_with_timeout(pid2, m, 500 /*ms*/)\n```\n\n## Backlog\n\n- expand the API coverage\n  - fill the gaps of encoding/decoding the messages\n  - conform to the C Node protocol\n- higher level API\n  - message builder \u0026 reader (hiding away current position)\n- connected testing strategy\n- treat and test the project as a library\n- reconnects / actor interface design?\n- multiple connections per `EInterface`\n\n## Development\n\n- Linux, OSX, Windows build config via Premake\n- `vagrant up` if you don't want to install the dependencies yourself\n\n### Source Structure\n\n- [erl_interface_pony](erl_interface_pony) the Pony API to `ei_connect`\n- [src/otp_pony_node_c](src/otp_pony_node_c) - a slim wrapper around `ei_connect` (see below)\n- [demo](demo) a Pony \"main\" spike used to get familiar with the `ei_connect` API and bootstrap the project: connects to an Erlang node and awaits message tuples\n- [demo.exs](demo.exs) the OTP/Elixir counterpart to the Pony demo, which sends the expected messages\n- [build](build) build config generated via premake from [premake5.lua](premake5.lua)\n\n## Dependencies\n\n### ei_connect\n\n- http://erlang.org/doc/man/ei_connect.html [Apache License 2.0](https://www.erlang.org/about)\n- key API currently in use\n  - `ei_set_tracelevel`\n  - `ei_connect`\n  - `ei_xreceive_msg`\n  - [`ei_decode_*`](http://erlang.org/doc/man/ei.html)\n\n### Pony\n\n- https://github.com/ponylang/ponyc [BSD 2-Clause](https://github.com/ponylang/ponyc/blob/master/LICENSE)\n\n### Elixir\n\n- used for the demo\n- https://elixir-lang.org [Apache License 2.0](https://github.com/elixir-lang/elixir/blob/master/LICENSE)\n\n### Premake\n\n- simplified meta-build\n- http://premake.github.io [BSD 3-Clause](https://github.com/premake/premake-core/blob/master/LICENSE.txt)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fd-led%2Fotp_pony_node","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fd-led%2Fotp_pony_node","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fd-led%2Fotp_pony_node/lists"}