HackerTrans
TopNewTrendsCommentsPastAskShowJobs

rastrian

no profile record

Submissions

[untitled]

1 points·by rastrian·2 months ago·0 comments

[untitled]

1 points·by rastrian·5 months ago·0 comments

[untitled]

1 points·by rastrian·5 months ago·0 comments

[untitled]

1 points·by rastrian·6 months ago·0 comments

[untitled]

1 points·by rastrian·6 months ago·0 comments

Functional programming and reliability: ADTs, safety, critical infrastructure

blog.rastrian.dev
158 points·by rastrian·7 months ago·187 comments

Beyond the Nat: Cgnat, Bandwidth, and Practical Tunneling

blog.rastrian.dev
40 points·by rastrian·7 months ago·21 comments

Not Everything Should Be Easy

blog.rastrian.dev
2 points·by rastrian·7 months ago·0 comments

The Future of Software Engineering: Efficiency, Learning Velocity, Small Teams

blog.rastrian.dev
3 points·by rastrian·7 months ago·0 comments

comments

rastrian
·2 months ago·discuss
A multi-server Discord control plane that exposes Discord operations as MCP tools, resources, and prompts for AI clients, with a built-in conversational AI agent.
rastrian
·5 months ago·discuss
TLDR: Pragmatic argument against outsourcing taste to tech influencers. Includes a framework for choosing communities/mentors, plus a critique of naive “build in public” and why private iteration + strong support/contingency beats transparent roadmaps early on.
rastrian
·6 months ago·discuss
Take a look at Algebraic Effects concept, I think you would like it.
rastrian
·6 months ago·discuss
I really don’t like the argument calling “industrial usage” just because a main company or FAANG aren’t using the tech stack, but arbitrary under the hood are doing basically the same stuff with internal toolings that should be entirely under the language features, not under a system design library.

But your take about modern Java is correctly, and they adopt this style under internal projects for some workflows.
rastrian
·6 months ago·discuss
heisen-valves are a perfect comparison, thank you.
rastrian
·6 months ago·discuss
Mostly “boring” stuff where the type system pays rent fast:

- Domain/state machines (payments/fulfillment-style workflows): modeling states + transitions so “impossible” states literally can’t be represented. - Parsers/DSLs & config tooling: log parsers, small interpreters, schema validation, migration planners. - Internal CLIs / automation: batch jobs, release helpers, data shapers, anything you want to be correct and easy to refactor later. - Small backend services when the domain is gnarly (Servant / Yesod style) rather than huge monoliths.

If you’re learning it beyond CS exposure, I’d start with a CLI + a parser (JSON/CSV/logs), then add property-based tests (QuickCheck). That combo teaches types, purity, effects, and testing in one project without needing to “go full web stack” on day 1.
rastrian
·6 months ago·discuss
You can get most of the “ADT/state-machine reliability” benefits in Python by combining static checking + tagged unions + boundary validation:

Model states as tagged unions (Union + Literal + dataclass(frozen=True)), use match (Py3.10+) and add assert_never so type checkers complain when you forget a case.

Run Pyright (strict) or mypy –strict in CI so “illegal states” show up as build failures, not incidents.

Validate/parsing at boundaries (HTTP/queues) with Pydantic discriminated unions (tagged unions at runtime), then keep internals typed.

For expected failures, prefer an explicit Result (e.g., returns) over exceptions-as-control-flow.

Use Ruff for lint/consistency (it’s not a type checker, but pairs well with one).

References here:

Pyright: https://microsoft.github.io/pyright/ mypy --strict: https://mypy.readthedocs.io/en/stable/getting_started.html PEP 634 (match): https://peps.python.org/pep-0634/ assert_never & exhaustiveness guide: https://typing.python.org/en/latest/guides/unreachable.html typing_extensions (backports): https://typing-extensions.readthedocs.io/ Pydantic discriminated unions: https://docs.pydantic.dev/latest/concepts/unions/ returns Result: https://returns.readthedocs.io/en/latest/pages/result.html Ruff FAQ: https://docs.astral.sh/ruff/faq/
rastrian
·6 months ago·discuss
I think we mostly agree for the nullable case in a sound-enough type system: if Foo | null is tracked precisely and the compiler forces a check before x.bar, then yes, you’re not “remembering” checks manually, the compiler is.

Two places where I still see tagged/discriminated unions win in practice:

1. Scaling beyond nullability. Once the union has multiple variants with overlapping structure, “untagged” narrowing becomes either ambiguous or ends up reintroducing an implicit tag anyway (some sentinel field / predicate ladder). An explicit tag gives stable, intention-revealing narrowing + exhaustiveness.

2. Boundary reality. In languages like TypeScript (even with strictNullChecks), unions are routinely weakened by any, assertions, JSON boundaries, or library types. Tagged unions make the “which case is this?” explicit at the value level, so the invariant survives serialization/deserialization and cross-module boundaries more reliably.

So I’d summarize it as: T | null is a great ergonomic tool for one axis (presence/absence) when the type system is enforced end-to-end. For domain states, I still prefer explicit tags because they keep exhaustiveness and intent robust as the system grows.

If you’re thinking Scala 3 / a sound type system end-to-end, your point is stronger; my caution is mostly from TS-in-the-wild + messy boundaries.
rastrian
·6 months ago·discuss
lmao
rastrian
·6 months ago·discuss
I mostly agree: for many businesses, a big SaaS outage and a payments outage can look similar in impact (lost revenue, interrupted operations). It’s not “life or death” most of the time.

The reason money-related systems often get singled out is the combination of irreversibility and auditability: a bad state transition can mean incorrect balances/settlement, messy reconciliation, regulatory reporting, and long-tail customer harm that persists after the outage is over.

That said, my point isn’t “finance is special therefore FP.” It’s “build resilience and correctness by design early”, explicit state machines/invariants, idempotency/reconciliation, and making invalid states hard to represent. Doing this from the beginning also improves the developer experience: safer refactors, clearer reviews, fewer ‘tribal knowledge’ bugs.
rastrian
·7 months ago·discuss
I think your Option/String example is a real-world tradeoff, but it’s not a slam-dunk “untagged > tagged.”

For API evolution, T | null can be a pragmatic “relax/strengthen contract” knob with less mechanical churn than Option<T> (because many call sites don’t care and just pass values through). That said, it also makes it easier to accidentally reintroduce nullability and harder to enforce handling consistently, the failure mode is “it compiles, but someone forgot the check.”

In practice, once the union has more than “nullable vs present”, people converge to discriminated unions ({ kind: "ok", ... } | { kind: "err", ... }) because the explicit tag buys exhaustiveness and avoids ambiguous narrowing. So I’d frame untagged unions as great for very narrow cases (nullability / simple widening), and tagged/discriminated unions as the reliability default for domain states.

For reliability, I’d rather pay the mechanical churn of Option<T> during API evolution than pay the ongoing risk tax of “nullable everywhere.

My post argues for paying costs that are one-time and compiler-enforced (refactors) vs costs that are ongoing and human-enforced (remembering null checks).
rastrian
·7 months ago·discuss
Yep, in practice a lot of orgs treat reliability as a cost center until an outage becomes a headline or a regulatory incident. I’ve seen the same tension in payments/banking: product pressure wins until the risk is visible.

Part of why I like “make invalid states unrepresentable” approaches is exactly that: it’s one of the few reliability investments that can pay back during feature work (safer refactors, fewer regressions), not only during incidents.
rastrian
·7 months ago·discuss
I’ve worked in Brazilian banking stacks that were literally FTP + spreadsheets for years. So yes, the ecosystem is often messy and protocols can be flaky.

That’s exactly why I argue for stronger internal modeling: when the boundary is dirty, explicit state machines/ADTs + exhaustiveness + idempotency/reconciliation help ensure bad feeds don’t silently create invalid internal states.
rastrian
·7 months ago·discuss
I get why it reads like FP evangelism, but I don’t think it’s “ignoring decades of prior art.” I’m not claiming these ideas are exclusive to FP. I’m claiming FP ecosystems systematized a bundle of practices (ADT/state machines, exhaustiveness, immutability, explicit effects) that consistently reduce a specific failure mode: invalid state transitions and refactor breakage.

Rust is actually aligned with the point: it delivers major reliability wins via making invalid states harder to represent (enums, ownership/borrowing, pattern matching). That’s not “FP-first,” but it’s very compatible with functional style and the same invariants story.

If the TS example came off as “types instead of validation,” that’s on me to phrase better, the point wasn’t “types eliminate validation,” it’s “types make the shape explicit so validation becomes harder to forget and easier to review.”
rastrian
·7 months ago·discuss
I think you’re both pointing at the same tradeoff: “untagged” unions feel lighter, but you often pay it back in ad-hoc narrowing (shape checks/heuristics) and ambiguity once variants overlap.

Tagged unions/ADTs make the discriminant explicit, which is exactly why they tend to be reliability-friendly: exhaustive matches + explicit constructors reduce “guessing” and refactor breakage.

That said, I agree the ergonomics matter, TS-style discriminated unions are basically “tagged” too once you add a kind field, for example.
rastrian
·7 months ago·discuss
Agree, I didn’t give testing enough space. A proper treatment would’ve doubled the post, so I’m writing a separate follow-up on testing.

Pure functions/immutability help a lot because tests become representative and cheap. I’d only push back on “tests of all equivalent scenarios” being sufficient, the space explodes and many real failures live at I/O/concurrency/distributed boundaries. My intended claim is that FP/ADTs/types reduce the state space and improve the ROI of tests, not replace them.
rastrian
·7 months ago·discuss
Agree on the economics. I’m not arguing for full formal proofs; I’m arguing for low-cost enforcement of invariants (ADTs/state machines/exhaustiveness) that makes refactors safer and prevents silent invalid states. Human processes will always drift, so you enforce what you can at the system boundary and rely on reconciliation/observability for the rest.
rastrian
·7 months ago·discuss
Agreed, I conflated FP with “typed FP.” My claim is mainly about static types + ADTs/exhaustiveness improving refactors/review/tests. Racket can get FP benefits, but absent static typing you rely more on contracts/tests (or Typed Racket), which is a different reliability tradeoff.
rastrian
·7 months ago·discuss
I get your point about ICFP drifting into “types, types, types.” I don’t think FP benefits are only static typing or immutability, pure-ish core/imperative shell, and explicit effects matter a lot even in dynamic languages.

My angle was narrower: static types + ADTs improve the engineering loop (refactors, code review, test construction) by turning whole classes of mistakes into compiler errors. That’s not “what FP is”, it’s one very effective reliability layer that many FP ecosystems emphasize.
rastrian
·7 months ago·discuss
I was searching for the Stefik article to argue here, thank you.