Written by: Dmytro Chasovskyi
Original article published on GitHub
What is that?
Railway Oriented Programming (ROP) is a functional programming technique that allows sequential execution of functions, not necessarily synchronous. The key concept is that each function can only accept and return Container
of either Success
or Failure
. Failure
wraps Throwable type and Success
can be of any type.
Visual representation of ROP
Therefore, in the simple scenario each function can go from Success
to either Success
or Failure
scenario and Failure
can go to Failure
scenario.
In the real world, you may want to transform Failure
to Success
, not only to Failure
. This procedure is called rescue
or recovery
; the name may vary from language to language.
Visual representation of ROP detailed is
It is important that you can transform from each state to each other state.
How to use in practice?
This concept is present but not promoted in Kotlin since Kotlin 1.3. The matter of fact Kotlin KEEP states that feature may change in the future. At the time of writing this blog post, Result<T>
is a Container
-like type which can be used inside generic containers such as Collection<T>
.
fun capitalize(cities: List<String>): List<Result<String>>
In the meantime, Result<T>
can be parameter of functions.
fun map(Result<String>): List<String>
- Why can't 'kotlin.Result' be used as a return type?
- How to allow Result<T> to be return type in Kotlin?
Result<T>
data-type has docs.
How to apply to existing code?
Example of functions that can have this paradigm and how to write adapters to those functions:
- Single track functions. Functions with no errors and no possible exceptions. See ComplexNumber and ComplexNumberResult examples.
- Dead-end functions. Functions which throw errors. Such as OutOfMemoryError or StackOverflowError. See HtmlParser and HtmlParserResult examples.
- Functions that throw exceptions. This is especially useful with IO operations. See DownloadPage examples.
- Supervisory functions. Functions which logs and supervise existing code, not necessarily return the result. See SentryLogin and SentryLoginResult examples.
All classes have corresponding tests, so you can compare difference in testing. Since adapters are primitive the more real-world example is into Application. The idea is to download HTML pages of URLs and recover in those cases when URLs are not valid.
Acknowledgements
Thank you for all inspires of this blog post. To my colleagues, especially to Thomas Borgen how showed my this technique in Python. Also, thank you Thorsten Heller who supported me and inspired to put in text. Also, to my first and patient listeners Cloudwheel team!