Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It'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.


At least in Erlang there is a standard state machine built-in module: 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.


UnrealScript 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.


> 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.


I 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?


We needed a state machine to manage app startup in Ardour, and initially (since we're using C++) we used a boost library for this.

It was pretty cool - you just create a text based table describing state transitions and various methods that execute before, after and as a result of the transition, and it generates all the code for you. It was very, very quick to get things basically working with this template library.

But then ... we had to start dealing with corner cases and some fiddly little details of precisely how this FSM was supposed to work.

We still have the table in a comment in the code, because it's probably one of the easiest things to read to understand the FSM, but the implementation is now just the usual C/C++ nested switch statements.


For 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


I 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).


Why 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.


mbeddr[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/




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: