Join the discussion

Write your take first — we'll ask for email only when you're ready to publish.

  • Hacker News
  • Could someone compare this to ST in Idris?
  • Yes, I believe it should be possible for someone to do that.
  • This is not really ST. This is analogous to eating at an old school restaurant.

    You can't just walk in to the food service counter and say "give me a burger"; you need to first get a ticket from the cashier proving that you've ordered a burger and then provide that ticket to the guy at the counter.

    That's literally the type state pattern

  • If you are asking in the context of Idris 2.0 (the current version), ST is not really related.

    However, if you mean ST in Idris 1.0, there is a definite correlation. The mechanism that ST used for enabling local mutations was very similar to the mechanism that the typestate pattern in Rust is using. ST was a framework for formalizing State Machines in dependent types which is the mechanism TFA is analyzing.

  • Maybe I am missing something but where is the entire source code?
  • Hi everyone! I am one of the authors (Falk), so feel free to ask me questions :)
  • Types increases particular types of correctness. There are more types of correctness.

    Our apps are built on what we call features. Our own in house database Dip participates in the correctness enforcement exercise.

    We built what we call an architecture compiler arcc which is a glorified linter (intentionally underselling) but enforces CQRS violations and other architectural violations at compile time.

    Query features cannot invoke Command features by construction. Queries cannot even invoke a Dip insert/update/remove().

    Our tooling now ensures all CRUD Dip.insert/update/query/remove() now accepts and returns appropriate schema types. It also ensures all Features.invoke() also accepts and returns appropriate handler types. arcc also enforces that a feature cannot even do Dip CRUD on an alien collection/table other than the feature leaf's owned collection.

    Pushing this further we are increasingly approaching a state where entire implementations compress to literal names of features and nothing else.

    The endgame is blank src/ for a massive ERP backend.

    Note: zero ai in code. Pure architecture.

  • I use Typestates and Newtypes extensively. The metric that shows Typestate and Newtypes are beneficial is: How many method calls or parameters can be called / used that compile but are not valid use cases. You want to minimise this number. I love having a type state where I can only make 1 or 2 method calls because the state enforces there are only a few parsing / validation / transition methods available. And there is only one valid way to supply the parameters, I cannot use the strings in the wrong location. I only wish we had named parameters like ObjC.
  • Check 'bon' crate for named parameters built on newtype.
  • From a language design perspective I go back and forth on named params. I think the only conclusion I’ve reached is that I am not in favor of them being optional, but I think that is more a concern for implementation of the language and less about how it effects users.

    How do you find the feature useful in this instance, I can’t quite picture how that works for typestate pattern functions.

  • You deserve a medal for that.
  • This was a talk at the FUNARCH workshop at this year’s ICFP.

    Here’s the livestream: https://www.youtube.com/live/c0pw1iVs_Q0?is=hwm2xa4cZOcqF5tW

    Well post the individual talks in the following days!

  • Talk starts as 6:01:10
  • Types are puzzles. A good Rustacean will make sure that the pieces fit to make the picture.

    That's why in crates where I need to make sure certain functions are called in order, I use a Ticket<T>, where one function returns a Ticket<Func1Done> with the output and the other has to consume it as an input.

    The typestate pattern is a specialization of making only valid states representable

  • why not just create a wrapper type for the payload that is returned by func1 and func2 takes it as a parameter?
  • One of my favorite useful patterns
  • > where I need to make sure certain functions are called in order

    The ticket approach is a neat way to handle this, but I’ve always felt that functions needing to be called in a specific order is usually a bad code smell.

    I’m sure there are times it’s unavoidable or maybe even the cleanest approach, but I don’t think I’ve encountered one in my career. When are you finding you need to do this?

  • I think this is similar to what Axum does to make sure your router has its state declared before you try to instantiate it. Are there other examples in popular libraries?
  • I’ve been experimenting with Rust’s type state pattern—I’m trying to build something that builds an inventory of some object storage prefix (recording the version and size of each object in the prefix), but the pattern seemed so cumbersome. The goal was to avoid committing to a particular I/O color (sync vs async) and to have a testable no_std core, but I have so much less confidence in the typestate version compared to the “define traits for I/O and build an imperative loop around it”. I’m curious if anyone has suggestions (I realize it’s probably difficult to help without access to source code).
  • I know this wasn’t the crux of your post, but do you find that you primarily look at types as puzzles in a majority of your code? I have a fundamentally different view and find other perspectives interesting when thinking about language design.

    As a separate point, I think this is an excellent example of making invalid states unrepresentable.