Found it in https://link.springer.com/article/10.1007/s40996-020-00512-8. Just look at (c) :))

Happy Railway

Hadi Lashkari Ghouchani
ProAndroidDev
Published in
4 min readJan 11, 2021

--

This post is on the tail of Railway Oriented Programming in Kotlin by Antony Harfield. So you need to read it first and continue here. As it’s obvious I really liked it and tried it out. It needs every process have a result like

The generic result

Then we could chain the processes and create our railway like

input to ::parse then ::validate then ::send otherwise ::error

It’s so satisfying and shows what we want to achieve so clearly until we notice the Result is not native to every processes. It’s a general wrapper. For instance the Result of ::validate will be more informative if it would be like

The result of validate method

By informative I mean the caller of this method will easier/cleaner branch its code with when expression/statement. The same can be applied to send method. The more native Result for ::send can be like

The resut of send method

But in these cases we will lose our clean code(Not “The Clean Code”) to show what is actually going on. To solve it you may need to go and read Railway Oriented Programming in Kotlin again, where by using Railway Oriented Programming we actually want to clarify what the happy path of the proccess is. This implies chaining the Successes on the above results can bring us an almost similar clean code as before. But how?!

If we try to write the happy path in plain English it would be like

parse input
- if failed return proper error message
validate
- if there is an invalid email address return proper error message
- if subject is empty return proper error message
- if body is empty return proper error message
send email
- if there is a IO problem return proper error message
- if there is a Timeout return proper error message
- if unauthorized, return proper error message

Do you read it easily? Can you find the happy path at the first glance? It’s because of separation of the happy path from the failures by indenting the failure lines. That’s how our brain trained to read fast. If we could manage to write our code like this then the happy path will be clear for our brain at the first sight. We will call it a concise code. The first take away from the concise code above is that the successes define the happy path. The second one is that the success result is one and only one but the failures can be many. Also we just returned the proper error messages on all branches, but potentially you can do anything in those cases. The practical usecase can be replacing the failure with the default value where we will come back to it later.

Proposal

In the concise code above I see some kind of when expression, but it distinguishes the success result from the failures. It’s not an ordinary when expression in Kotlin. But Kotlin is a powerful language where supports DSLs. Maybe we can create a DSL to do this job for us.

For instance let’s try to create a DSL for ValidationResult. This DSL must returns the happy result and have something like when statement to branch the code for different failures. Something like

ValidationResult DSL

Note this DSL must use inline functions to be able to break the flow and do something like below with return

Validate in doWork method

To create this DSL we need a builder

DSL builder

Also we need to define the elseIf extension method like this

Validate DSL extension function

It’s a proof of concept DSL. Notice the result is lateinit which makes this DSL exhaustive on runtime, but unfortunately Kotlin DSL doesn’t have mandatory annotation or something like that, so we cannot force it on compile time :|

However, we can do the same for SendResult so will be able to call the send method like this

Send DSL

Then the doWork method will look like

Complete doWork method

where into is

inline infix fun <T, R> T.into(block: (T) -> R): R {
return block(this)
}

which its definition is the same as let with infix modifier. It’s the best implementation that I came with where it arranges the failures to have an indent respect to the happy path, so probably it’s more readable to our brain.

Also did you notice the input of validate and send methods. They force the caller to finish the previous step successfully, which is fantastic. What do you think?

But who would pay for the boilertrap code the DSL needs! To solve this last problem, you can checkout Happy code generator. Also you can find above example with more details in the tests of this repository here.

More Complex Scenario

Let’s have a concise code like

parse input
- if failed return proper error message
validate
- if there is an invalid email address return proper error message
- if subject is empty fix it by replacing the default subject
- if body is empty return proper error message
send email
- if there is a IO problem return proper error message
- if there is a Timeout return proper error message
- if unauthorized, try to authorize then send it again.

As you can find out, there are two changes respect to the previous concise code. First in case of the empty subject we can fix the email, we will replace it with default subject, and the second one is the unauthorized failure can be fixed by trying to authorize and resend the email. In the end, we can use the above DSL and sealed classes before with a little bit of changes, but the concept is the same. So the code will be like

Complete doWork method for more complex scenario

Can you read it at the first glance? Again the failures are indented respect to happy path. By the way, you can find the complete code of this example in the previous repository but with different commit here.

Hope you enjoy coding more with this DSL. Don’t forget to upvote this post and please leave some feedbacks on comments. Thank you for your time!

--

--