Hacker News

Top stories

Live mirror
30 storiesupdated just nowView source snapshot
  1. Claude discovers a novel enzyme system with CRISPR-like repeats(anthropic.com)
    458comments
  2. Linux support is coming to Snapdragon X2 Series(qualcomm.com)
    12comments
  3. VSCode's SSH Agent Is Bananas (2025)(fly.io)
    60comments
  4. We just shipped support for the ugliest part of HTTP: Vary – Cloudflare Blog(cloudflare.com)
    3comments
  5. Fixing the Portobello Police Station Clock(pointinthecloud.com)
    84comments
  6. LensVLM: Compressing long context as images, expanding only relevant pages(huggingface.co)
    3comments
  7. Italian parliament votes for return to nuclear energy(apnews.com)
    329comments
  8. The mystery animal on an ancient god's head(signoregalilei.com)
    9comments
  9. A brief history of Windows scroll bar shortcuts(devblogs.microsoft.com/oldnewthing)
    41comments
  10. Mercury 2.5 LLM hits 770 tokens per second(artificialanalysis.ai)
    6comments
  11. Jev in 25 Lines of Python(nobodywho.ai)
    194comments
  12. The Curious Power of Punctuation(newyorker.com)
    2comments
  13. Gemini 3.8 text-to-speech(blog.google)
    118comments
  14. Radicle: Disclosure of Vulnerability in the Network Protocol(radicle.dev)
    43comments
  15. Tokens too cheap to meter(jyn.dev)
    175comments
  16. Making Tailscale Faster(tailscale.com)
    9comments
  17. I don't want the details(michaelheap.com)
    190comments
  18. Swap, ZRAM, Zswap and Hibernate on NixOS(matthewbrunelle.com)
    4comments
  19. Stripe's Knowledge AI Platform(stripe.dev)
    105comments
  20. Z80 REPL (2018)(abagames.github.io)
    18comments
  21. Claude Code reads AGENTS.md only when telemetry is on [fixed](szypowi.cz)
    243comments
  22. Show HN: I built a post-mortem debugger for native Windows x64/x86 crashes(forensicdbg.com)
    2comments
  23. Once Claude can measure something, it can make it faster(claude.dev)
    90comments
  24. Bwbach, My Guardian Goblin(robertmay.photography)
    5comments
  25. A refined phylochronology of the second plague pandemic in Western Eurasia(pnas.org)
    discuss
  26. QuestDB (YC S20) Is Hiring a Sales Engineer(questdb.com)
    discuss
  27. 28% of job postings on company career sites have been open over 90 days(unlisted.careers)
    277comments
  28. UK military jamming other nations' satellites to defend itself, BBC told(bbc.com)
    175comments
  29. Seattle City Council votes to ban surveillance pricing in sale of groceries(consumerreports.org)
    169comments
  30. White House Says Access Is a 'Privilege' in Court Filing Defending Media Ban(nytimes.com)
    5comments

What is railway oriented programming? (2020)

29 pointsby 3y agoblog.logrocket.com
17 comments
3y agoHN ↗

Maybe I'm not understanding this, but you're saying that you should only ever return success or failure from functions but then you're also throwing exceptions? how do these patterns reconcile with this railroad programming style in the video?

3y agoHN ↗

THe introduction talks about throwing, but the rest of the text is about returning Success and Failure values. It's probably just a poor choice of terminology in the intro.

3y agoHN ↗

It’s about adapting methods that throw out of your control. For example, I/O or timeouts in JS (presentation is language agnostic).

3y agoHN ↗

I don't understand what that paragraph is on about. I use this approach extensively and the whole point is that you don't need exceptions anymore. LogRocket is a service that kind of counts on errors being thrown so maybe there's something there haha

3y agoHN ↗

The spirit of the idea is to be explicit about what is the behavior you can expect from the code. You can’t foresee every exception, but you can at least make an effort to handle those that are likely to happen. It allows the calling code to handle the exception in a way that it doesn’t kill the program unless necessary. You can recover from some exceptions and should if possible.

Note that it took me a bit of getting used to the idea due to my background with python. Its type system does require a lot of extra code to implement this pattern.

3y agoHN ↗

Yes, extremely confusing article imo.

The use of promises and async await allow the unhandled rejections to bubble up, but they've put "Success" else "Failure" in their pseudocode which could be interpreted as a return (wouldn't bubble up without extra code in the callers) or a throw (would bubble up) - not filling in that part leaves too much room for interpretation as does using async await everywhere unless you are already somewhat familiar with what they're driving at.

My understanding of what's trying to be conveyed would be better explained by using something like the Result type in Rust paired with the ? operator (failures are returned early/bubble up but successes go through). They should have slapped together a result type in Typescript to get the point across.

Similarly in go you have multiple returns and the common `if err != nil { return err }` a billion times which I think is the same concept here, just much more verbose. How does that differ from the initial code in tfa? I'd say it's because the caller returns the callee's failure rather than constructing its own thing or continuing with logic.

Then there's also some monad discussion that I'm not fully qualified to give.

3y agoHN ↗

The idea is that instead of checking error after every call you just throw exception and handle it above but article is just plainly bad.

3y agoHN ↗

i must say i found this pretty incomprehensible - the railoroad track illustrations i found particularly hard to understand.

3y agoHN ↗

The main point is that your function can only return either a success or a failure. Failure should be handled using the throw statement so as to throw an exception, while success is what leads to another function, which can be of any type.

Rust bakes this into the language with the Result type. You signal railway oriented programming by returning a Result, which contains either the thing you want or an error. If you try to get the thing, you must deal with the possibility of an error in some way. There is no half-way.

3y agoHN ↗

This analogy/metaphor is usually represented in code via an interface called MonadFail (e.g. in Haskell https://hackage.haskell.org/package/base-4.17.0.0/docs/Contr... and Scala https://www.javadoc.io/doc/org.typelevel/cats-docs_2.13/late... )

Some related points:

- Sequential composition (putting one piece of track after another) is captured by the Monad interface. Monad is more general than MonadFail, since it includes things which don't fit into the 'two track' analogy.

- Concurrent composition (running separate tracks side-by-side) is captured by the Applicative interface. Applicative is more general than Monad, since it includes things which can't be sequenced.

Where possible, we should try to use the most-general interface we can: this makes our code usable in more scenarios, and also prevents us calling inappropriate methods. In fact, this article's example of validating multiple fields not a good use-case for 'railway oriented programming', since it will abort after detecting one failure!

A better approach is something like the Validated type from Scala's cats library https://www.javadoc.io/doc/org.typelevel/cats-docs_2.13/late...

Validated can represent success/failure, but it does not provide the Monad interface; hence we cannot sequence one check after another. The only way to combine multiple Validated results is via its Applicative interface, which performs all the checks concurrently: if they're all successful, the result is successful; if any fails, the result contains all of the failures.

In the article's example, if we have an invalid email address and a missing first name, we'll only be told about the email address. In the Applicative approach, we'll be told about both problems.

(Note that we can also define an ApplicativeFail interface; but I don't think the railway metaphor makes sense in that case)

3y agoHN ↗

The code examples don't make any sense, the functions may return a error but they are not being checked for in the next function. Using thir own metaphor, their functions may deviate to the red rail, but then the next function just assume everything is always coming from a green rail, they don't codify the real possibility that the input is from the red rail.

For me it seems like a method that may work with the right language sugar, but without a sugar made specific for this you will have to pollute all functions with extra error handling to handle the possible red-rail-input. At this point it should be better to just handle those errors when they happen, and quit the happy path there, instead of following along the "happy path" just passing errors to all the following functions.

3y agoHN ↗

I got the impression that the author of the article didn't actually understand the idea they were writing about. It reads like blogspam whose real purpose is not to convey understanding but to contain advertisements for, in this case, LogRocket.

3y agoHN ↗

Yes, this article (the OP article that is) is horribly confusing and doesn't follow the ROP architecture at all.

The core idea is that instead of a Func that takes in a Foo and returns a Bar, you can take in a Maybe<Foo> and return a Maybe<Bar>.

Then you don't need error handling in the outer composition, once everything is of the form F = Maybe<T1> -> Maybe<T2>, etc, then you can fully compose everything at the outer layer into a F = Maybe<T1> -> Maybe<Tn>.

So then you just check for Ok() at the very end and unwrap and handle any errors there.

3y agoHN ↗

Yeah it makes loads of sense. Syntactic sugar required to make the medicine go down, of course :)

3y agoHN ↗

This looks just like goto-based error handling in C (e.g. [1]). But that is an invention of necessity, lacking better error handling mechanisms.

Conceptually, the "two track system" doesn't make much sense. It models the error handling as a linear routine, with monotonic jumps from the happy path to the error path. But is error handling really linear in the general case? If it's not what is the use of modelling it as a track? And are the jumps really monotonic as illustrated? If they are not, all the elegance attributed to this model goes away.

[1] https://www.xml.com/ldd/chapter/book/ch02.html#buierr