In this blog post, I talk about my experience exploring GitHub and RxGo, and a walkthrough to my learning perspective driven from a position of curiosity.
This blog post is intended for curious developers who might want to contribute to open source but are hesitant or intimidated by other skilled developers. Hopefully by the end of this blog post, you’ll have picked up a better perspective when approaching the idea of contributing to open source.
RxGo
It’s such a pleasant surprise to see one of my favorite libraries available on the language I’m mastering. I went over the repository and spent a few minutes reading and understanding its usage: What does it look like in Go? How does it compare to my previous experience (RxJS)?
What does it look like?
observable.ForEach(func(v interface{}) {
fmt.Printf("received: %v\n", v)
}, func(err error) {
fmt.Printf("error: %e\n", err)
}, func() {
fmt.Println("observable is closed")
})
It looks a little too verbose for my taste. I don’t particularly like the following notation:
func(v interface{}){ // your code here })
As compared to Javascript’s succint and daresay beautiful lambda functions:
e => // your code
The first thing that I do as I jump into this Go variant of the reactivex library is go straight for the documentation. Note that I have never used this library before; I’ve only used other variants like RxJS, RxJava.
I spend a bit of time reading the manual, and seeing how I might potentially assist in the improvement of this library. There’s a huge spectrum of help that an individual can do, and sometimes you don’t even need to be a developer perse to contribute! The best place to start is reading documentation, making sure you understand how to use the library, and helping others that may potentially be struggling to use it.
If you help save the library owners time answering newbie questions, you’re actually buying them time/space to work on the library!
Issues
After reading quite a bit, I wanted to gauge the extent of use that this library currently experiences. This can easily be estimated by the number of issues, pull requests, downloads, and stars an open source library has.
I go checkout the Issues tab on Github to see what things people had difficulty with, as they may be experiencing challenges of functional reactive programming or golang syntaxes which I feel comfortable enough to help with.
With only 9 issues in the repository, my hunch was that the intersection between Golang developers and Reactivex developers is small for now.
This is understandable for two reasons: Golang is new, and Reactivex takes time to learn.
Golang is part of the new wave of modern languages (relative to other languages: C++, Python, Javascript, PHP). Furthermore, Reactivex is also a small community of its own. It isn’t the case that there isn’t any clamor for Reactivex, but instead it is because gaining mastery over Reactivex takes some time to overcome a steep learning curve. This is due to the change of mindset. It’s a big shift of a paradigm from imperative programming to functional, declarative programming that many developers struggle with.
Learning the why behind the patterns that Reactivex promotes is one of my favorite curiosities to explore: not only in understanding how to use the library to follow the patterns it provides, but also to understand how the library was implemented. To me, they are simple joys that I appreciate in building neater, resilient, and easy to maintain code.
Contribution
After dwelling in the issues board for a bit, I went ahead and pulled a copy of the repository and helped out with a bit of typos on the documentation, and checked out other issues that I perceived to be within my means to assist in.
I went ahead and commented on issues which I feel I had enough experience in, properly qualifying my certainty and hedging my claims and just being very clear about what I know and do not know.
One specific error was a fascinating challenge: unexpected behaviour in combination with Serialize / withPool.
It was a curious scenario that mimol91 posed. He included sample reproducible code, so in my curiosity, I went ahead and ran it on the local copy of the git repository I just pulled (in one of the test files, so that I can easily use debug on VSCode!).
So I went ahead and watched the machine run the code and observed what the library did and what it meant.
I liked this approach to learning, because I am completely oblivious to what the library does. So I switched my hat from developer-that-uses-3rd-party-libraries to curious-engineer-that-starts-with-a-blank-slate. Having this mindset allows me to clear my mind from biases and anxieties about intimidating internal library code.
Running somebody else’s code is like going to a theater where the actors follow the script you provide (somebody else’s code) using the props you put on stage (3rd party libraries). There’s really no reason for me to be anxious- I simply need to get the popcorn and watch the show.
And so I did. It was like a movie, except it was interactive. I get to play detective!
I investigated the issue, and it turns out there was a side effect that occurred when using the serialize operator on observables that do not have any subscribers yet (which is not expected; chaining operators on observables should not mutate or affect other observables).
The point of declarative programming is being able to define what things are (functions, data structures, and code units) in a pure or idempotent manner. If we were to define three sets of numbers (namely even integers, odd integers, and all integers), defining one set should not mutate or change another set. The definitions must be pure.
Observables are data structures that can be observed. You can wrap a process (e.g. file reading) into something that can be observed. It can either read a sequence of byte data, throw an error, or complete the process (onNext, onError, onComplete).
Maintaining that constraint of functional purity when declaring different observables (meaning: their definitions do not mutate or affect each other) gives us RxGo library users that peace of mind that we can reliably expect how a observable will behave, and when it will actually occur or run code. Maintaining that constraint allows us that abstraction during software design and programming.
Summary
In the end, I summarized my findings:
The Observe
method consumes some of the elements of the original channel, thus losing elements on the subscribe. So it was not that the operators ‘mutated’ the original observable, but that it consumed emissions from the original observable (I guess it could be argued that the operators applied changes to the channel when it shouldn’t have).
To confirm that this was an error caused by emissions consumed by one channel and missing in another channel, applying the WithPublishStrategy()
on the FromChannel
constructor solves the issue and returns the desired list (all of the customers from 1 to 98).
The recommendation I had was to rework the Serialize
method so that it wouldn’t consume elements from the channel prior to any subscription. I deferred any actual implementation work until the author of the library teivah gave his feedback, as it is possible that I may have misunderstood how the library is to be used, or what the scope of capabilities it is intended to have (See Law 1: Never Outshine the Master).
Reflection
I had a fun time reviewing the provided code and reading the library’s implementation. In contributing to open source:
- I gain a better appreciation for the code that I intend to use (RxGo),
- I discover how library developers use design patterns and abstractions in their library implementation,
- I also learn more about the language itself: Go. I find peculiarities on how the language is used by the library developers.
The last point is super fun because the learning part is similar to a sudoku puzzle, or like a math equation with two known elements and one missing element:
- the library developer’s implementation, readable through source code
- the resulting behavior guaranteed by the library
- the reason why the library developer implemented it that way
It’s similar to math because I usually have the first two elements and don’t know the third element. Reading their code is like watching a detective movie, figuring out the motive based on small clues and evidences.
Of course, I come in and comment on a library like this but must respect the library owner’s opinions and expertise in the library that he wrote. Given that, I offered my diagnosis and asked that the library validate (or correct) the claims that I have made.
To reach that point of being comfortable of receiving criticism and mistakes being pointed out, take the time to understand deeply what you know and what you don’t know. Eventually, you’ll realize that people pointing out your mistakes or misunderstandings is easily one of the best ways to learn. In the case where someone corrects you but you were in fact correct, this creates the event or opportunity for you to better express oneself so that you can get better in communication (See Xanatos Gambit).
In any case, that was a fascinating experience and I’m glad to have shared it with you. I hope you enjoyed it!
Learning the why behind the patterns that Reactivex promotes is one of my favorite curiosities to explore: not only in understanding how to use the library to follow the patterns it provides, but also to understand how the library was implemented.
I hope you find the courage to make mistakes, to learn openly and humbly, and to express yourself fully to the world. Cheers.
Leave a Reply