More than a year ago, a friend introduced me to the concept of Railway-Oriented Programming (ROP). I liked the idea and the fluency that comes with it, but it was a style born out of functional programming - something that is not fully embraced in Java despite the introduction of lambdas and streams in Java 8.
Though there were already a few existing Java libraries for this, I thought it might be a good idea to write one myself as it was a good opportunity for me to:
- understand what ROP is
- practice TDD
- learn how to publish a Java library to a publicly available package repository (i.e. Sonatype Nexus)
- try to use GitHub actions to setup a pipeline and automatically publish the library to the package repository
After a couple of months and tons of procrastination, I was able to came up with something usable albeit not production-ready (that wasn't the goal anyway).
If you're curious, you can get the snapshot here.
I'm planning to do a write up on what JRail is, what it can do and what I think are its shortcomings (though I think what I've made is not a true ROP). But that's not what this post is all about. For now, here are some thoughts and highlights from the journey:
- Naming is (still) hard
- I suck at GitHub
- I don't think I truly understood Monads
- Spock is great but it didn't fit my needs
- Open-source Licensing Options
- Dipping my toes on GitHub Actions
- Creating and approving my own PR
- Publishing the library
Naming is (still) hard
As clichéd as it may be but naming is really hard. I've been mulling over the name for almost a week before I settled for JRail, since most Java things either starts or ends with "J". It was only after the creation of the repository that I realized it looks like a name for Japan Rail. But hey, it has already been created and I don't wanna spend another week thinking for a name.
I suck at GitHub
Prior to this, I don't have any serious repositories in my Github account. Most of what I have were more like scratch paper scribbles rather than showcase-worthy projects. So yeah, that's a pretty lengthy way to say I don't code outside of work.
Look at that contribution calendar. So impressive /s
It also took me an embarrassing amount of time to setup my SSH keys and the repository which should have only taken a few minutes if not seconds. This is a nice refresher, I guess.
I don't think I truly understood Monads
And at this point, I don't think anyone does.
Kidding aside, I really wanted to understand the underlying theory in this. I believe it's perilous to simply do something without understanding why it is done in such a way. I remember consuming a lot of materials about category theory and functional programming when I first ventured into this project, but wasn't able to retain much information (my brain isn't great).
The closest I got to understanding it is with this explanation.
So I tried explaining it my own words to a friend, because "the best way to learn is to teach", right? But I couldn't answer his questions, so perhaps I really didn't get monads after all.
Like a normal person, I gave up. I may come back to it later if I ever had the motivation again but for now I think I've learned enough to proceed. I then shifted my focus on how I can make the API intuitive enough to use, of course with the help of tests.
Spock is great but it didn't fit my needs
You've probably heard of the testing framework called Spock, which is written in Groovy. It's great and I love using it everyday for work. So I initially wrote all my tests using it.
But I realized that having a dynamically-typed language doesn't help me that much in designing the APIs (the not-so-strict Java generics isn't helping either). I also wanted to know what it's like to use the APIs I've made. So to avoid shooting myself on the foot, I decided to rewrite the tests with JUnit instead.
Another reason is that I wanted to let my tests serve as a documentation or a compilation of examples. I wrote this library with Java devs in mind, and having tests in Groovy is sort of a step towards a different direction. One of the few differences is Groovy closures where the syntax for lambda expressions uses curly braces instead of parentheses.
{ closureParameters -> statements }
While that may not be so significant for an experienced dev, I didn't want to add any additional cognitive load to the readers anymore. Writing the tests in Java made the "examples" clearer and consistent.
Open-source Licensing Options
Why the heck are there too many of them? Thankfully, GitHub created https://choosealicense.com/ to help noobs like me pick the proper license.
It's unlikely that people are gonna use this anyway. But just in case, I wanted to let them know that I don't care what they want to do with it and I'm not liable whatever chaos they caused.
Dipping my toes on GitHub Actions
It's my first time to setup my own CI and it was actually easier than it is to type this whole blog post. GitHub has already provided some workflow templates to get you started so it really is just a matter of clicking. It's also fairly easy to play around with since it's just YAML (as most DevOps things are).
Creating and approving my own PR
I don't think I need to explain this. This is the best part of this project
Publishing the library
This is the most exciting yet frustrating part of the project. I was only able to publish a snapshot, but I couldn't sign it properly. After multiple failures in my CI, I decided to skip the signing and just publish a snapshot. I didn't want to publish an unsigned package but I was too lazy to continue. I felt like I've already done what I ought to do so this is already enough of an achievement for me.
Yup, I'm lazy. I know.
Final Thoughts
Building JRail was a pretty fun exercise despite my failure to complete it. I may comeback to it later to improve it and to revisit its paradigm since I don't think I've followed ROP. But realistically speaking, I'll probably just leave it as it is because I usually prefer to stay away from coding after work hours. It's just my own way of balancing things and taking the time to enjoy some other stuff.
Nonetheless, it's still a nice reminder for myself to always practice the basics and to sometimes explore what else I can do other than those that I learn on the job. I think I'll borrow some principles in ROP and apply them to our code.
Further Reading
In case you're interested and wanted to try it or do some reading on your own, here are some of resources to get you started:
- Railway-oriented Programming by Scott Wlaschin. This is the original source of the idea as far as I know.
- How to Publish Open Source Java Libraries to Maven Central by Emil Kirschner. I read other guides too but his is the easiest to follow despite it being written in Kotlin
- GPG Cheat Sheet
- A bunch of functional-programming articles
- Functors, Applicatives, And Monads In Pictures by Adit Bhargava
- Your easy guide to Monads, Applicatives, & Functors This really looks cool with its animations
- This answer in Stackoverflow
- Category Theory for Programmers by Bartosz Milewski. I haven't finished reading this book but it's really interesting especially if you're interested in the abstract topics of math
Top comments (3)
If you haven't already seen this, the OSSRH has detailed step by step instructions for publishing artifacts including videos explaining the steps: central.sonatype.org/publish/publi...
They even break it down by build tool so you can find some gradle specific stuff there. I use maven so can't really help if your problem is gradle related.
Since you've gotten as far as publishing a snapshot, I'm guessing your problem is likely related to pgp signing. Did you distribute your public key? If not then that will prevent your artifacts from releasing from staging since they need to be able to verify your signature.
I initially had issues due to that. The maven central documentation suggests using MIT's key server. But that didn't work for me. Try Ubuntu's key server instead: keyserver.ubuntu.com/ which worked for me.
Woah never saw that guide before. Thank you very much!
And yup, I did distribute my pgp key on some servers but I wasn't really sure if it was successful.
I'll try your suggestion one of these days.
You're welcome. Try searching for your public key on the Ubuntu key server. If it doesn't find it then it wasn't distributed successfully.