4 ms·
I spent the first bit of this article going "Wait isn't that just what a finite state machine is?" and then was subsequently super satisfied by the next bit whe
by advael 2y ago
I spent the first bit of this article going "Wait isn't that just what a finite state machine is?" and then was subsequently super satisfied by the next bit where they say that this is indeed a really good way to handle the general case
Reducing program logic to a small state machine and business logic that interacts with it in predefined ways is a pattern I learned while doing asynchronous netcode for a game project, but gradually realized was applicable in a ton of places, and framing it as a robust version of a "plan-execute" pattern is really intuitive but also really powerful. Great article
- scotty79 2y agoIt's quite sad that generał purpose language designers are not yet at the stage of daring to provide any convenient syntax for high level concepts like state machines or entity component systems.
- rdtsc 2y agoAt least in Erlang there is a standard state machine built-in module: https://www.erlang.org/doc/system/statem.html https://www.erlang.org/doc/system/statem.html Most of TLS support in the built-in library is implemented using gen_statem, on top of the lower level crypto primitives provided by OpenSSL, same for SSH support. Others have built Raft using it. It's quite nice and I appreciate that it comes as a standard module.
- advael 2y agoFor both cases I think it's less that there isn't convenient syntax for it, and more that the generality of the syntax doesn't restrict you enough to enforce the pattern. For example, you can do a decent job approximating a monadic state machine system in C++14 with just optional and structs, so long as you follow the rule that all mutable state is kept in a state struct and all business logic must be in the form of functions with the signature optional<state>(const state&). Hell, you could even remove the optional if you don't consider there to be any "failure" cases, just cases that don't mutate the state Then, you can use any container you like to keep track of your valid state transitions and not even have to use polymorphism. But of course C++ gives you many ways in which to trivially break this nice system of guarantees, including mutable lambdas, or even just implicit reliance on fixed memory addresses via explicit allocation and pointers in an external scope. This pattern is very general, so it's hard to capture in a guardrail
- deleted 2y ago[deleted]
- wavemode 2y agoI would argue that the domain of language design is quite a bit lower-level than the domain of specific design patterns like state machines or ECS. A language that wants to support ECS, shouldn't implement ECS as part of its syntax. It should provide features that allow library developers to implement ECS (in an ergonomic and efficient way).
- scotty79 2y agoWhy not? When defining a class I should have to be able to have implicit collection of objects of this class with each field of the class kept in its own separate array. It's about memory organisation. And languages certainly feel free to impose those with stack, heap and colocation of all fields of an instance regardless of how varied they are in meaning and usage.
- munificent 2y agoUnrealScript has (had?) built-in syntactic support for state machines. I think Jonathan Blow had some ideas for language support for ECS in Jai, or at least support for structure-of-array stuff. I'm not sure if it panned out. My experience with both of those patterns is that each implementation tends to have enough unique requirements that it's hard for a language to find a sweet spot where built-in syntax can cover enough real-world use cases. For example, off the top of my head, with state machines I've had questions like: * Can states be defined dynamically or are they all known at compile time? * Likewise, is the set of possible transitions fixed (and declarative) or freely specified at runtime? * Does every instance of a given state machine always have the same set of states or do some vary? * Can you attach arbitrary data to states? Arbitrary behavior? * Can you attach arbitrary data to state transitions? Arbitrary behavior? * Is the predicate logic for determining when to perform a state transition declarative and built-in, or can arbitrary imperative code cause a state transition? * Are transitions synchronous? Asynchronous? Either? * Can outside code observe when transitions occur in some way? * Is the current state publicly accessible or encapsulated? * Are states first-class objects? Are transitions? I've tried just to build a reusable FSM library and even that wasn't very successful because there's just so much variance in each implementation.
- scotty79 2y ago> Can states be defined dynamically or are they all known at compile time? You can say that about classes as well, give different answer and have languages that support one of those or both. Similar questions exist about a lot of concepts that landed in some or many languages, like objects, async, events, promises, continuations. In most of them we achieved some sort of fuzzy consensus of what their capabilities should be.
- munificent 2y agoI actually don't think we have achieved any kind of consensus here which is why there are a proliferation of popular languages. Concurrency is handled in wildly different ways between C/C++/Java/C# (threads), Go/Lua (goroutines/coroutines), JavaScript/Dart (async promises), etc. Object-oriented programming is interpreted so differently by different languages that people can't even agree on what the term means. Do you have multiple inheritance or not? Are methods always virtual, virtual by default, or non-virtual by default? Is there overloading? What control do you have over inheritance? Are there interfaces or not? How are static methods handled?
- pie_flavor 2y agombeddr[0] has a state machine language extension. It ends up being really useful primarily because of the IDE's table view for the data flow, vs the normal case-by-case nested flow. [0]: http://mbeddr.com/ http://mbeddr.com/