Thank you for your insightful and well written article! I appreciate the depth of golang experience required to express your arguments above. Unfortunately, I know little regarding Erlang so my comments will solely focus on the golang code.
As evidenced by your “finalizer” solution, it’s unnecessary to directly couple the Parent Goroutine’s Context to the Map’s Goroutine Context. However, instead of implementing the Dict’s destruction using golang’s runtime.SetFinalizer solution, one could return the “context.cancel()” function and execute it via golang’s defer statement, as this statement usually implements “resource cleanup”. Any released resources produced by defer’s action that are no longer reachable will be eventually garbage collected by golang.
ex: d, release := NewChanDict(); defer release()
Above provides behavior similar to “finalizer” and in this form becomes idiomatic as there are a number of types like files, channels, contexts, … that rely on defer for signaling and/or resource deallocation. Finally, this technique enables the testing of Dict’s destruction to be as easy to test as any other golang implicitly GC resource.
Regarding golang and Erlang links, I typically compose solutions as pipelines. Pipelines are networks of cooperating goroutines such that each one in the pipeline physically couples at runtime to the other upstream goroutine(s) that produce the input(s) a given goroutine requires to generate its outputs. To me these are explicit relationships, as I have to bind output channels to input ones, where an upstream goroutine performs the role of a producer while the connected (by channel) downstream goroutine(s) represent a consumer(s). The relationship, from a Producer’s perspective, can be abstractly defined as “a Producer streams resources to one or more Consumers” while from a Consumer’s perspective, “a Consumer feeds on resources provided by one or more Producers”.
The interesting aspect of this relationship is the complete dependence of the Consumer on its immediate Producer(s). Therefore, when the Producer(s) dies, the Consumer will eventually die as it can no longer “feed” itself. In other words, for a given goroutine, if all of its required input channels are exhausted and closed then it’s time to terminate itself. The remainder of the network will eventually terminate due to the cascading outcome of individual downstream goroutines (Consumers) deciding to terminate themselves due to the death of their upstream Producers. With this understanding, only a very few, if any, goroutines in a network need a separate “close” channel to signal termination.
Given the cursory understanding of Erlang, I won’t say binding goroutines via their channels is equivalent to Erlang links, but channel binding can offer similar behavior without introducing other abstractions like links and nodes.