{"id":39039123,"url":"https://github.com/jtaylorcpp/gerl","last_synced_at":"2026-01-17T17:46:24.648Z","repository":{"id":76587147,"uuid":"150415958","full_name":"jtaylorcpp/gerl","owner":"jtaylorcpp","description":"Golang implementation of the fun parts of OTP","archived":false,"fork":false,"pushed_at":"2021-03-16T00:40:10.000Z","size":138,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-06-20T10:10:43.822Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jtaylorcpp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-26T11:28:20.000Z","updated_at":"2022-02-01T18:45:21.000Z","dependencies_parsed_at":null,"dependency_job_id":"9c17971f-5319-4f4b-9020-3f7444cf81ce","html_url":"https://github.com/jtaylorcpp/gerl","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/jtaylorcpp/gerl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtaylorcpp%2Fgerl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtaylorcpp%2Fgerl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtaylorcpp%2Fgerl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtaylorcpp%2Fgerl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jtaylorcpp","download_url":"https://codeload.github.com/jtaylorcpp/gerl/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtaylorcpp%2Fgerl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28513975,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T13:38:16.342Z","status":"ssl_error","status_checked_at":"2026-01-17T13:37:44.060Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2026-01-17T17:46:24.315Z","updated_at":"2026-01-17T17:46:24.637Z","avatar_url":"https://github.com/jtaylorcpp.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"GERL \n========\n\n\n[![CircleCI](https://circleci.com/gh/jtaylorcpp/gerl.svg?style=svg)](https://circleci.com/gh/jtaylorcpp/gerl)\n\nGerl is an attempt to build out the remarkable parts of the Erlang/OTP for Go\nwhile keeping in spirit with the language.\n\nThe vision is to provide a way to build, schedule, and manage both locally and globally\navaialble processes and their ability to communicate.\n\nGerl provides functionality for:\n\n  - process id\\'s\n\n  - generic server (gen_servers)\n\n  - message passing between pids\n\nThis is mainly done by using:\n\n  - channels\n\n  - go routines\n\n  - grpc\n\n## Why\n\nGolang is a ton of fun. Its reads and writes easily with a good balance of performance and has a robust community around it. Golang is a daily driver.\n\nHowever, Golang does have a list of issues or features missing that often makes people look at and learn new languages. \n\nOn such journey took me down the path of learning Erlang. Erlangs legedary status of reliable deployments and everest like learning curve made a stark contrast to Golang. Through the hours and days of unraveling the Erlang mystery; there were a few features that, once you got the hang of, made you wonder why they didnt exist elsewhere.\n\nOne such set of features is the event driven and message passing process. Message passing is baked into the essence of Erlang as is the spirit of functional programming. Yet the combination of the two into the Erlang gen_server is a piece of magic. Quickly define the handlers for certain types of messages and spin up a hyper-lightweight process. Networking...provided. Event driven messaging...use the handlers. State management...included in the server init. \n\nMAGIC.\n\n## Basic Concepts\n\n### Process ID (Pid)\n\nProcesses IDs (pid) is the main abstraction for communicating\nwith a running process. The pid contains channels for bidirectional communication \nfrom the running process and, under the hood, handles the GRPC implemtation. \n\nThe pid has both an *inbox* and *outbox* which are channels used to pass messages\ninto a processes handler/main loop. This allows for the go-routine running the process\nto get to the message once it is done handling other messages.\n\nAll messages sent to a pid are blocking with repsect to the GRPC server implementaiton.\n For *casts*, which to a process appear to be non-blocking, have an empty message returned\nat the GRPC layer which forces both pid to be able to confirm a message was passed.\n*Calls* return a new message at the GRPC layer and will wait until a process gets to and \nprocesses the message.\n\n### Generic Server (genserver)\n\nGeneric servers, genservers, are a concept directly pulled from Erlang/OTP. A genserver\nis a process that has a pre-specified set of functionality; mainly a *call* and *cast*.\n\n\n*call* is a bidirectional action in which a client sends a message to and expects\na result back from a genserver. The genserver has a specific function dedicated\nto handling *call* actions.\n\n*cast* is a unidirectional action in which the client sends a message to a genserver\nand moves on.\n\nThe genserver client builds the GRCP client necessary to make the calls and needs the \naddress of the pid of the genserver to send messages back and forth.\n\n### Process (proc)\n\nProcesses are another concept borrowed from Erlang/OTP. In this case, process has a \nhandler which is started as a go-routine and the pid, as with a genserver, is the\nmain way to communicate with the running process.\n\nAll messages to processes are intentionally unidirection and processes must be designed\nto allow for bi-directional communicate. Although less featureful, the genserver is the\nchild of the process in which the genserver implements opinionated and strict constraits on\nthe process idea.\n\n\n## Getting started\n\n### GenServer Ping Pong\n\n```go\nimport (\n\t\"log\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jtaylorcpp/gerl/core\"\n)\n\nvar PongAddr PidAddr\n\nfunc defaultCast(_ core.Pid, _ core.Message, _ FromAddr, s State) State {\n\treturn s\n}\n\nfunc pingCall(pid core.Pid, msg core.Message, fromaddr FromAddr, s State) (core.Message, State) {\n\tlog.Println(\"ping description: \", msg.GetDescription())\n\tswitch msg.GetDescription() {\n\tcase \"serve\":\n\t\t//run ping\n\t\tlog.Println(\"ping sending to pong\")\n\t\tlog.Printf(\"fromaddr\u003c%v\u003e pongaddr\u003c%v\u003e msg\u003c%v\u003e\\n\", fromaddr, PongAddr, core.Message{Type: core.Message_SIMPLE, Description: \"ping\"})\n\t\tpong := Call(PongAddr, FromAddr(pid.GetAddr()), core.Message{\n\t\t\tType:        core.Message_SIMPLE,\n\t\t\tDescription: \"ping\",\n\t\t})\n\t\tlog.Println(\"ping go msg: \", pong)\n\t\treturn pong, s\n\tdefault:\n\t\tlog.Println(\"ping unknown message: \", msg)\n\t\treturn core.Message{}, s\n\t}\n}\n\nfunc pongCall(_ core.Pid, msg core.Message, fromaddr FromAddr, s State) (core.Message, State) {\n\tlog.Println(\"pong description: \", msg.GetDescription())\n\tswitch msg.GetDescription() {\n\tcase \"ping\":\n\t\tlog.Println(\"pong got ping\")\n\t\tdesc := msg.GetDescription() + \" pong\"\n\t\treturn core.Message{Type: core.Message_SIMPLE, Description: desc}, s\n\tdefault:\n\t\tlog.Println(\"pong unknown message: \", msg)\n\t\treturn core.Message{}, s\n\t}\n}\n\nfunc TestGenServers(t *testing.T) {\n\tgs1 := NewGenServer(\"genserver 1\", pingCall, defaultCast)\n\tgs2 := NewGenServer(\"genserver 2\", pongCall, defaultCast)\n\n\tgo func() {\n\t\tt.Log(gs1.Start())\n\t}()\n\n\tgo func() {\n\t\tt.Log(gs2.Start())\n\t}()\n\n\ttime.Sleep(50 * time.Millisecond)\n\n\tPongAddr = PidAddr(gs2.Pid.GetAddr())\n\n\tlog.Println(\"ping server addr: \", gs1.Pid.GetAddr())\n\tlog.Println(\"pong server addr: \", gs2.Pid.GetAddr())\n\tlog.Println(\"var pong server addr: \", PongAddr)\n\n\trmsg1 := Call(PidAddr(gs2.Pid.GetAddr()), FromAddr(\"localhost\"), core.Message{\n\t\tType:        core.Message_SIMPLE,\n\t\tDescription: \"ping\",\n\t})\n\n\tt.Log(\"pong test: \", rmsg1)\n\n\tif rmsg1.GetDescription() != \"ping pong\" {\n\t\tt.Fatal(\"pong test failed\")\n\t}\n\n\trmsg2 := Call(PidAddr(gs1.Pid.GetAddr()), FromAddr(\"localhost\"), core.Message{\n\t\tType:        core.Message_SIMPLE,\n\t\tDescription: \"serve\",\n\t})\n\n\tt.Log(\"ping test: \", rmsg2)\n\n\tif rmsg2.GetDescription() != \"ping pong\" {\n\t\tt.Fatal(\"ping serve test failed\")\n\t}\n\n\tgs1.Terminate()\n\tgs2.Terminate()\n}\n\n``` \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjtaylorcpp%2Fgerl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjtaylorcpp%2Fgerl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjtaylorcpp%2Fgerl/lists"}