{"id":19416025,"url":"https://github.com/pbayer/erjulix","last_synced_at":"2025-04-24T12:33:15.720Z","repository":{"id":49875634,"uuid":"374744066","full_name":"pbayer/erjulix","owner":"pbayer","description":"Connect Erlang, Julia and Elixir","archived":false,"fork":false,"pushed_at":"2021-07-28T17:50:29.000Z","size":177,"stargazers_count":14,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-03T05:01:38.965Z","etag":null,"topics":["elixir","erlang","interop","julia","messaging","udp"],"latest_commit_sha":null,"homepage":"","language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pbayer.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":"2021-06-07T17:12:31.000Z","updated_at":"2024-11-26T06:17:10.000Z","dependencies_parsed_at":"2022-08-12T20:50:31.211Z","dependency_job_id":null,"html_url":"https://github.com/pbayer/erjulix","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/pbayer%2Ferjulix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pbayer%2Ferjulix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pbayer%2Ferjulix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pbayer%2Ferjulix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pbayer","download_url":"https://codeload.github.com/pbayer/erjulix/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250628877,"owners_count":21461707,"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":["elixir","erlang","interop","julia","messaging","udp"],"created_at":"2024-11-10T12:45:15.831Z","updated_at":"2025-04-24T12:33:15.458Z","avatar_url":"https://github.com/pbayer.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Erjulix\n\nConnecting Erlang, Julia, Elixir\n\n[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://pbayer.github.io/erjulix/stable)\n[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://pbayer.github.io/erjulix/dev)\n[![Build Status](https://github.com/pbayer/erjulix/workflows/CI/badge.svg)](https://github.com/pbayer/erjulix/actions)\n[![Coverage](https://codecov.io/gh/pbayer/erjulix/branch/master/graph/badge.svg)](https://codecov.io/gh/pbayer/erjulix)\n\n## Project\n\nThis is my ambitious little project to connect the different worlds of Erlang/Elixir and Julia:\n\n- Provide one package for three platforms/languages,\n- Allow them to talk to and call each other.\n\nNow Erlang and Elixir processes can send messages to each other since they run on the same BEAM platform and share PIDs. But how about sending messages to Julia and back to Erlang/Elixir?\n\n## A sample session\n\nIn the Julia REPL we start a `pServer` task, which on demand spawns an `EvalServer` task with its own module namespace.\n\n```julia\njulia\u003e using Erjulix, Sockets\n\njulia\u003e pServer(6000)\nTask (runnable) @0x0000000110b30ab0\n```\n\nIn the Elixir REPL we request a Julia `EvalServer` and use it to \nevaluate Julia expressions or to call Julia functions.\n\n```elixir\niex(1)\u003e {:ok, jl, _} = :ejx_udp.srv(6000)  # get an eval server from Julia\n{:ok, {{127, 0, 0, 1}, 54465}, \"Main.##esm#257\"}\niex(2)\u003e :ejx_udp.eval(jl, \"using .Threads\")\n{:ok, []}\niex(3)\u003e :ejx_udp.call(jl, :threadid)\n{:ok, 3}\niex(4)\u003e :ejx_udp.call(jl, :factorial, [50])\n{:error,\n \"OverflowError(\\\"50 is too large to look up in the table; consider using `factorial(big(50))` instead\\\")\"}\niex(5)\u003e :ejx_udp.eval(jl, \"\"\"    # define a function on the Julia server\n...(5)\u003e function fact(x)\n...(5)\u003e     factorial(big(x))\n...(5)\u003e end\n...(5)\u003e \"\"\")\n{:ok, \"Main.##esm#257.fact\"}\niex(6)\u003e :ejx_udp.call(jl, :fact, [50])\n{:ok, 30414093201713378043612608166064768844377641568960512000000000000}\niex(7)\u003e :timer.tc(:ejx_udp, :call, [jl, :fact, [55]])\n{527,\n {:ok,\n  12696403353658275925965100847566516959580321051449436762275840000000000000}}\n```\n\nThe last timing shows that the ping-pong for calling the created Julia `fact` function with data from Elixir and getting the result back takes roughly 500 µs with both sessions running on the same machine (MacBook Pro).\n\n```elixir\niex(8)\u003e a = Enum.map(1..10, fn _ -\u003e :rand.uniform() end)\n[0.9414436609049482, 0.08244595999142224, 0.6727398779368937,\n 0.18612089183158875, 0.7414592106015152, 0.7340558985797445,\n 0.9511971092470349, 0.7139960750204088, 0.31514816254491884, 0.94168140313657]\niex(9)\u003e :ejx_udp.set(jl, :a, a)  # create variable a on the Julia server\n{:ok, []}\n```\n\nBack in the Julia REPL:\n\n```julia\njulia\u003e exmod = Erjulix._ESM[1]  # get access to the server module\nMain.##esm#257\n\njulia\u003e exmod.a                  # and to the created variable a\n10-element Vector{Any}:\n 0.9414436609049482\n 0.08244595999142224\n 0.6727398779368937\n 0.18612089183158875\n ⋮\n 0.9511971092470349\n 0.7139960750204088\n 0.31514816254491884\n 0.94168140313657\n\njulia\u003e using Plots ....\n```\n\n### Working remotely\n\nIf we start our `pServer` with the machine's IP address and a key, communication with remote clients gets SHA-256 encrypted:\n\n```julia\njulia\u003e getipaddr()\nip\"192.168.2.113\"\n\njulia\u003e key = Erjulix.genpasswd(12)\n\"1XQeFem2NUNw\"\n\njulia\u003e pServer(getipaddr(), 6000, key)\nTask (runnable) @0x00000001110e7b90\n```\n\nWe use the machine's IP address and that key to access the `pServer` from a Raspberry Pi in the local network:\n\n```elixir\niex(1)\u003e :inet.gethostname()\n{:ok, 'raspberrypi'}\niex(2)\u003e key = \"1XQeFem2NUNw\"\n\"1XQeFem2NUNw\"\niex(3)\u003e {:ok, jl, _} = :ejx_udp.srv({{192,168,2,113}, 6000, key})\n{:ok, {{192, 168, 2, 113}, 55052, \"j8Gh3G6dPfJm28UpthL0dXew\"}, \"Main.##esm#258\"}\niex(4)\u003e :ejx_udp.call(jl, :factorial, [20])\n{:ok, 2432902008176640000}\niex(5)\u003e :timer.tc(:ejx_udp, :call, [jl, :factorial, [20]])\n{86620, {:ok, 2432902008176640000}}\n```\n\nThe `pServer` generated a new key for encrypted network access to the Julia `EvalServer`. The timing shows that network ping-pong took under 100 ms between the two machines (without encryption it takes around 70 ms).\n\n```elixir\niex(9)\u003e :ejx_udp.client(jl, :exit)\n{:ok, :done}\n```\n\n## Rationale\n\nThis is a prototype for interoperability based on [Erlang`s Term Format](http://erlang.org/doc/apps/erts/erl_ext_dist.html) over UDP. \n\n- It is aimed at experimenting and learning before providing Julia [Actors](https://github.com/JuliaActors/Actors.jl) with functionality for sharing messages with Erlang/Elixir.\n- It allows applications in Web services, IoT or microservices.\n- A more general application, providing message-based interop also with other languages should be done with [OSC](http://opensoundcontrol.org).\n\n## Caveats\n\n**Thread-safety:** Of course accessing the server module as demonstrated is not thread-safe and thus should not be done concurrently.\n\n**Security:** If you share UDP-Server addresses and ports, a remote client can get access to the filesystem. If you provide \na key to the `pServer`, data transmissions will use SHA-256 encryption.\n\n## ToDo\n\n- [x] Implement [JWT](https://jwt.io) tokenized secure data transmission,\n- [ ] Implement an Elixir server to serve Julia with Elixir/Erlang functionality.\n\n## Dependencies\n\n- The Julia package currently depends on [`ErlangTerm.jl`](https://github.com/helgee/ErlangTerm.jl).\n- The Erlang/Elixir part depends on [a fork](https://github.com/pbayer/jwerl) of `jwerl`, compatible with Erlang/OTP 24. There is [an issue](https://gitlab.com/glejeune/jwerl/-/issues/18) to update the main repo.\n\n## Installation\n\nWhen available in the Julia registry, you can install the package with\n\n```julia\npkg\u003e add Erjulix\n```\n\nIf [available in Hex](https://hex.pm/docs/publish), the package can be installed in Elixir by adding `erjulix` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:erjulix, \"~\u003e 0.1.0\"}\n  ]\nend\n```\n\nDocumentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)\nand published on [HexDocs](https://hexdocs.pm). Once published, the docs can\nbe found at [https://hexdocs.pm/erjulix](https://hexdocs.pm/erjulix).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpbayer%2Ferjulix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpbayer%2Ferjulix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpbayer%2Ferjulix/lists"}