-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Idea: Enums as State Machines #1906
Comments
It makes more sense to use session types here |
Some previous work based on state machines that might be worth a look |
Thanks! I will take a look those links :) |
@mgattozzi The "State Machine Patterns" article you posted is interesting. In general, I like the idea of using the type system to encode program constraints and enforce them statically (after all, what else are type systems for :P). Interestingly, at the end of the article, the author says
which happens to be this proposal... I would also argue that my proposal is more ergonomic and more easily readable, too. Also, I am still looking at the session types reading... |
I wonder how this relates to #1450. They are similar in that both of them have this aspect of certain operations only being available in contexts where you (or rather, the typechecker) know which variant the |
Rather than building a special-purpose syntax for this, have you looked at the proposals around enum refinement? That would let you define |
Yes, I think that is the more general observation that makes my proposal feasible. I would be very interested in knowing what other ways these sorts of situations can be useful... Actually, if we could find a way of exposing this sort of extra knowledge to the user, I think we could enable all sorts of really cool static guarantees about code. In other words, we would be allowing the user to specify constraints that the compiler can already prove, so there would be little extra overhead in the compiler but the user would get extra guarantees. @TedDriggs I believe you are referring to something similar to this post mentioned above. While I think the design is nice, I think that ultimately, it is a way of getting around existing limitations. This proposal would make state machines more compact and easier to read... |
@Manishearth I finally got around to reading a bit about session types. It seems like session types are more geared at communication protocols. In particular, it seems like the main offering of session types that is not present in this proposal is that session types allow you to define the relationship between to two state machines more clearly (i.e. state machine A can only make certain transitions after a specific set of transitions by state machine B). I would be down to augment this proposal with such a feature, if people feel it would be really useful. It does seem like something that could be added afterwards, though. |
You misunderstand me, session types are already expressible in Rust, I'm suggesting that if you need to express complex state machines then session types may help, instead of shoehorning stuff onto the enum system. |
Ah, I see. I disagree, though. Most things can be expressed with Rust's type system if you put in enough effort, since it is Turing complete, IIRC. That doesn't make it convenient or elegant to do so, though. For example, the session types library you pointed out is expressive, but it often forces you to use types like this: // Offers: Add, Negate, Sqrt, Eval
type Srv =
Offer<Eps,
Offer<Recv<i64, Recv<i64, Send<i64, Var<Z>>>>,
Offer<Recv<i64, Send<i64, Var<Z>>>,
Offer<Recv<f64, Choose<Send<f64, Var<Z>>, Var<Z>>>,
Recv<fn(i64) -> bool, Recv<i64, Send<bool, Var<Z>>>>>>>>; This sort of thing makes me wince a bit. Also, type-check can be exponential time for some of these more unusual uses of the type system. Also, I would argue that the enum representation still seems a bit more compact than the session types library. Finally, I didn't think that I had really "shoehorned" that much. Enums are a very natural and intuitive way to express different states, and adding transitions relations onto them seems like a logical addition to me, but maybe that's just me 😛 |
That's incorrect. Turing completeness gets you the ability to run arbitrary computations in the type system, nothing more. Turing completeness is but one small facet in the world of expressiveness. The goal of type systems is not to do computation, it is to express and enforce constraints, and expressiveness is viewed through that lens. It's a common fallacy, but basically Turing completeness doesn't actually mean much in many contexts where it gets invoked.
Right, because it's a pretty generic library. For a regular state machine you can just use some phantom types and express these constraints with a limited set of conversion methods. Hyper does this (or used to), and it's pretty easy overall.
Your proposal reintroduces typestate; the dependence of the type and its behavior on the contained value. Typestate is an interesting concept (which Rust had many years ago) but probably not one that fits well into Rust now -- most use cases of typestate can be adequately handled with phantom parameters. It also introduces typestate for a very specific situation, enums-as-state-machines, and thus feels very bolted-on/shoehorned to me. If this were to happen I think the more natural way would be through #1450, where you can then hang conversion methods off the type. But mind you, you can do that with phantom (or nonphantom) types now anyway, kind of. |
Ah, I see my mistake. Thanks for correcting me! But the spirit of my comment still holds. My point was just that we often want to introduce sugar for something extremely useful, even if it is technically already possible. For example, technically, rust is able to express closures without the
I think we simply disagree on how generic/useful state machines are. I also don't have a real grasp of the implications of typestate, and I also don't want to bloat/complicate the language. So my inclination is to close the issue if nobody else is in favor of it...
I am not sure I understand this point. Is the session-types library an example of this? |
No, this has nothing to do with that -- I do feel that state machines are pretty useful. I'm saying that the solution proposed here feels bolted-on. Enum-types feel like a more holistic solution, and they are useful for many other things. FWIW there are plans for a JS generator-esque thing with closure-like syntax and
Not exactly. |
Yes, it was #1823 that first got me thinking about this... |
I believe restricting transitions is too inflexible a restriction. In particular We've previously discussed subtyping relationships among
These statically subtyped
I think those discussions considered extending
|
Hmmm... your comment reminds me of the Union types in Pony. It seems that that was the direction #1450 was taking, too (that's what untagged unions are, right?). And indeed, that does strike me as a more general solution than either my proposal or session types. |
I've run into a situation where subtyping or extending |
There was also an idea that linear types could provide types for which you must manually call a destructor. I suppose those ideas (a) passed through #1180 before becoming the much less radical |
Hmmm... that's like a bit of a round-about way of defining a set of accepting states in an NFA, though. I would still rather that there was some sugar... But on the whole, the discussion here has convinced me that it would probably be good to just wait and see what new type-system features come about... |
There was a less ergonomic but more powerful approach suggested in trait field's RFC in which a trait could act like an enum :
|
I think that #2033 solves this mostly... |
The idea is nice, but I don't think it's usage would be popular. So in purpose of not overcomplicating the language I suggest adding mandatory enums may have some other useful compiler extensions like:
or
|
I'm going to go ahead and close this now to reduce clutter... |
The problem of encoding some sort of state machine is common for many programs, such as building parsers, schedulers, protocols, or coroutines. This proposal extends enums to allow them to express state machines.
Enums would be extended with extra syntax as follows. The design is intended not to break any existing code. Enums would retain all of the features they currently have, but they would also have extra abilities. For example
I think it should be relatively easy to enforce these constraints in safe code statically with little or no non-local reasoning. (Not 100% sure about this, though... Can someone find a counterexample?). For example,
The text was updated successfully, but these errors were encountered: