Egel is based on graph rewriting semantics, at any point the program forms a directed acyclic graph of combinators and evaluation is achieved by trampolining the root node. The Egel language is a bare-bones front-end to that semantics, roughly comparable to a lambda calculus with constants where constants compose.
Because of this semantics, a number of things are more 'easy' to implement than in more traditional languages. Egel is implemented in C++ without an explicit garbage collector, parallel evaluation is simply spawning of another root node, and the graph -or parts of the graph- can be serialized, shipped, or saved to disc.
Distributed programming involves picking up parts of the graph and shipping those to a remote node for evaluation. There are various manners in which the model allows for a distributed implementation but I went for a relatively straightforward solution, given the runtime I already had.
Servers, and clients, are implemented atop of Google's Protobuf. Preceding every call, the client does a best-effort scan of the graph and sends a bundle of referenced combinators, the code, to the server. After that, the term is sent and evaluated on the server which will send a term back. Referencing combinators that are not present for whatever reason (for instance, opaque objects like file handles or dynamically loaded c code) is undefined behaviour.
Now for some code, the following defines a server, it starts a service and then blocks waiting for remote calls.
import "egel_rpc.ego"
using System
def main = rpc_server "localhost:50001"
Given such a server, a client can ask for terms to be evaluated. Because the Egel language doesn't have process constructs, we capture what is to be evaluated remotely within a lambda.
import "egel_rpc.ego"
using System
def main =
let C = rpc_client "localhost:50001" in
rpc_call C [_ -> [X -> X] ] 42
In the above example, a lambda is sent to server, that returns the identity function to the client, and the client applies that to the constant 42.
Another example, we generate a list of values on the server, and sum that list on the client.
import "prelude.eg"
import "egel_rpc.ego"
using System
using List
def main =
let C = rpc_client "localhost:50001" in
rpc_call C [_ -> from_to 1 1000 ] |> sum
It works reasonably well but I am still wrinkling out the features.Note: the Egel interpreter is a hobby project and in beta. It is roughly useable but slow and everything is still subject to change.