Join the discussion
Write your take first — we'll ask for email only when you're ready to publish.
- Hacker News
- Completely free flow typing is risky in terms of interpretability, but type narrowing - var a : supertype; if (a is subtype) { // a is known to be subtype }, or type case, saves boilerplate in any OOP language.by waldrews
How exactly does it work? Is this a typo?out (; balance == balance + amount) // checked after method returnsby diath- I've never used D, but it appears to be valid syntax. https://dlang.org/spec/function.html#postconditionsby lgas
- Looks like its a typo :(
The correct way to go about this would be to return the new balance and capture the return value in the first part of the out postcondition like:
```D double deposit(double amount) in (amount > 0, "Deposit amount must be positive") out (result; result == balance) { balance += amount; return balance; } ```
My mistake!
by prydt - A genuine question: is the first point (flow typing / type narrowing) a subset of or intersection with or just an alias to SSA (static single assignment)? I'm playing with a small interpreted language implementation that is based on Lua, and have reached a point where I want to implement a single-pass SSA (there is a nice short CS paper on this), but cannot get my head around all the concepts, even if I need proper SSA for Typescript-like usability.by buybackoff
- SSA = static single assignment?
I am confused
- What's the nice short paper? (I'd be interested in reading it!)by prydt
- At least in my understanding of SSA, its a compiler implementation detail which makes writing optimizations simpler. I imagine you can implement flow typing without SSA.
Can you elaborate what you mean?
by prydt - No. Type systems are unrelated to abstract machines which are unrelated to usability.
Type inference/checking happens early in the pipeline.
SSA is a way of laying out assembly instructions for an abstract machine. I say abstract because real machines re-assign values to the same addresses over time (which is precisely what 'single' static assignment prescribes against). Once you know which registers your real machine has (and instructions), you could take your SSA and turn it into real assembly.
Also, "single-pass SSA"? Not to be too pedantic, but SSA is the destination, not the journey. You could take a single pass to transform from some expressions or statements into SSA, or perhaps from SSA into something else. What's the paper?
by mrkeen - So while the ideas discussed are interesting, the origins are a bit off.
Flow typing, is actually called Flow-sensitive typing.
Contracts were introduced into the industry via Eiffel, which continues to be sold via Eiffel Software company.
By the way, at the recent DConf 2026, during the panel discussion, contracts was actually one of the features that were discussed as something that they would remove from the language, if doing it all over again.
Rust's borrow checker, is based on Affine Types, and the first systems language that looked into it was Cyclone, which AT&T started as research project in colaboration with an university, to eventually replace C.
by pjmlp - Could someone explain the appeal of flow typing?
I can see how it can be useful to start with a broad type, e.g. a union, and narrow it down in a block. However, I don't quite get the opposite direction shown in their example (first an int, then a string, then a union).
by aix1 - Same rationale as flow valuing. Some people like values being reassigned, and some people like types being reassigned.
You might be reading too much into the union example. The checker just doesn't know if the middle block ran, so maybe it remained an int, or maybe it became a string.
by mrkeen - Nice list. I have a new language I'm working on (called Zena: https://zena-lang.dev/) with all of these in some form:
If you have static types and unions, control-flow analysis and narrowing is critical for avoiding an excessive amount of casts - and if you also have pattern matching, you get very nice style where a type-check, state extraction, and branch are all one expression.
Borrow checking. Zena is a GC'ed language, but it runs in Wasm and lots of Wasm resources are external, so Zena has affine types and second-class values for managing resources and disposing of them when no longer used. GC + borrowing is a great combo because you don't need borrowing for everything and lexical lifetimes with a few escape hatches cover most things. The ownership system is also great for modeling structured concurrency.
I'm working on contracts after borrow checking is complete. My impetus there is AI-generated code. If humans still review at all, reviewing the contacts more than the implementations makes managing large amounts of changes easier.
I'd like to see a few more good ideas spread:
Formal verification. Contracts should be a good stepping stone into a spec language, from there a proof language and checker. This should also be good for AI-generated code.
Numeric unit types / units of measure with dimensional analysis. We should be able to say that a variable isn't just a f64, but a f64 of meters, and when divided by seconds, give a velocity. I don't know why this hasn't made it into more mainstream languages, but it seems like it makes programs more clear, not just statically safer. For synax, my plan is to parameterize scalars by units, like f64<m> vs f64<s> and have units like `m` and `s` be associated with dimensions like `length` and `duration`.
Async cancellation. I added cancellation as a first-class language concept in Zena so that it can be handled like exceptions, but aren't exceptions. It extends try/catch to try/catch/cancel/finally. When a task is canceled, a cancellation unwinds the stack starting from the next suspension point (await). The benefit here is that you don't have to remember to check for cancellation in async functions - they're all cancellable.
by spankalee - Zena looks super cool! I was wondering if you could walk me through this syntax thats part of the example loops:
``` let iterator = items.[Iterable.iterator](); // <--- this part in particular is confusing me while (let (true, item) = iterator.next()) { console.log(`next: ${item}`); } ```
Dimensional types and formal verification make me super excited to see more of this language. You also probably mention this somewhere and I'm missing it, but any thoughts on adding pure functions / more general mutability enforcements?
- If you're serious about doing OO with static typing as well as (of course) mutability, you basically have to have something like flow typing to keep away the circle/ellipse nonsense. (In so far as flow typing is really static typing at all!)by leoc
- Yes... But I think this only tells half of the story.
What if you pass a reference and mutate the object inside the function?
by Garlef - I feel like languages are playing around different paints if coat mostly, and not trying to build more meaningful programming experiences.
I'd love to see a language whose pitch is that they have very next level stdlibs builtin. Effect for example is basically a mini stdlibs unto itself. It would be amazing to see such a principled deliberate craft applied to a language. Scope, layers etc etc etc etc: make visible, make first-class the actual pieces of computing, make them part of the language, explicitly modelled.
I'm also super excited for Zena, which just got announced yesterday! A typescript alike that compiles to wasm, and which really leans in to modern wasm, such as gc, wasi. A language that sits well at the cross-roads, that is excellent glue, that runs anywhere, that bridges other languages, is very compelling. https://justinfagnani.com/2026/09/09/zena-a-new-wasm-first-p...
- Most of my research conversations with Claude nowadays are basically about this—what it would take to make every latent bit of program semantics visible and expressible in the language itself. As you put it, first-class everything.
At this point I think we have good solutions for expressing pretty much all the most common program semantics, but there’s no language that brings them all together under a unified syntax, tooling, etc.
by jkhdigital - I know it's controversial but I really do love C++26's contract assertions.
I find they enable you, your consumers, IDEs, agents, etc understand the contracts of a method far faster, as it means you don't actually have to read the full method body. If the pre condition is correct and the post condition fails then you can be fairly sure the bug report goes to whoever owns that method, as either the precondition is wrong or the method is wrong.
by vallerie - I didn't expect this to get posted here. Long time lurker here.
I'm really interested in programming language design and ergonomics. What niche PL features would you like to see have more adoption?
by prydt - Generic narrowing types / linear types (like if you check that a string has length 10, then its type knows, and functions accepting bounded strings can accept it.)
This makes it easier to split raw inputs from validated inputs and delimiting where they are used in the code.
by jiehong - I’m a big fan of checked exceptions, which are niche in the sense that only Java has them (at least among popular programming languages). However, Java lacks the ability to parameterize code over sets of exception types, which places limitations on how checked exceptions can be used with type-generic code. That’s something that can be improved.
Exceptions allow more flexibility in separating the success-case program flow from the error-case program flow, compared to return codes or union return types. Unchecked exceptions, however, have the same drawbacks as dynamic typing does. Checked exceptions are the static-typing equivalent.
by layer8 - Row types are great. I'm doing a PureScript project right now and absolutely love having row polymorphism.
I'm also a fan of effect systems, although I haven't used them as much. Having an IO type in Haskell is great, but the ergonomics aren't (among other things, you get async-like function coloring). Effects seem like a much nicer, more composible way to get the same benefits.
by tikhonj - > Borrow Checking
It's very confusing name for this feature. It suggest that some sort of borrowing takes place and that it's just an optional check, which isn't the case. It should be named something like "enforced static usage analysis" instead.
In my programming language I have similar mechanism. But it isn't just checking, since it affects code generation by tracking which variables are still in use and which can be destroyed.
by Panzerschrek - In Rust variables are not destroyed after the last borrow ends but instead when it goes out of scope. I guess that is why it us called borrow checking.by jeltz
- You can very likely borrow check in languages that don't have it in the type system. Exactly the way you suggest, as an optional add-in. It's still WIP but in my side project I haven't found cases that can't be handled yet.by dnautics
- > It suggest that some sort of borrowing takes place
It does. A value is passed by reference, borrowing it from the owner.
> and that it's just an optional check
I don't see how the word "check" implies that it's optional.
by zahlman - In rust it's a lot closer to optional: you can in principle compile rust without doing any borrow checking at all (and I believe in practice mrustc does not bother to implement it, because it's primarily used for bootstrapping and so assumes it is already being passed code that compiles with regular rustc).by rcxdude
- For pedantry, should we note that design by contract came all the way from Eiffel ?
(But it's possible that even less people ever wrote Eiffel than D, so, who knows)
by phtrivier - Racket also added contracts around the same time that D did.by xedrac