7 ms·
Show HN: State Trooper – Tiny, no frills state machine for Go
Needed to isolate a bit of DRY logic for some of my projects. Might be of use to others out there.
- deleted 3y ago[deleted]
- victorbjorklund 3y agoGreat name!
- hishamk 3y agoThank you!
- giraffe_lady 3y ago[flagged]
- brigadier132 3y agoThis would be hilarious if it was satire
- giraffe_lady 3y ago[flagged]
- deleted 3y ago[deleted]
- Anon_Forever 3y ago[flagged]
- dang 3y agoCan you please see https://news.ycombinator.com/item?id=36384562 https://news.ycombinator.com/item?id=36384562 and stick to the site guidelines? You've unfortunately been breaking them lately. I'm copying the link here because the other thread is already a bit old and I want to make sure you see it.
- giraffe_lady 3y agodang sorry bro but the site guidelines are bad. People regularly endorse straight up atrocities, racism, eugenics, holocaust denial in here and that's all fine if they stay calm and don't say the slurs. I will never not be mad at people defending atrocities and that is the superior transgression. You value civility over human dignity. It's cowardly. Let me express anger. The people commenting this stuff here expect never to be challenged on it. Expect that they have the right to spew this stuff, carefully, without being told their actions are shameful. It's good for them.
- dang 3y agoWords like "human dignity" and "challenging" ae too grandiose to apply to internet attacks, putdowns and so on. If you want to do something for human dignity, there are plenty of other ways, some of which might even have a positive effect, unlike calling names and whatnot. We know what all that leads to, there's no moral value in it (regardless of how righteous the people doing it feel, or how they justify it) and we're trying to avoid that. If other users are posting bad comments, the thing to do is to downvote them and/or flag them and/or let us know about them at hn@ycombinator.com - as the site guidelines request. Posting flamewar attacks in response only evokes worse from others, draws more attention to the thing you're attacking (which energizes it, which contradicts your intention), and ultimately plunges the ecosystem into internet hell. If you don't want to help HN avoid that outcome, please don't post here. We don't come close to seeing everything that gets posted to HN. If you see a post that ought to have been moderated but hasn't been, the likeliest explanation is that we didn't see it. You can help by flagging it or emailing us at hn@ycombinator.com. https://hn.algolia.com/?dateRange=all&page=0&prefix=false&query=likeliest%20explanation%20by%3Adang&sort=byDate&type=comment https://hn.algolia.com/?dateRange=all&page=0&prefix=false&qu... Re "superior transgression": everyone always feels like the other person started it and did worse. I haven't used the word civility in many years except to explain why we don't use that word.
- oofta-boofta 3y agoTell me your a DEI Director without actually telling me you're a DEI Director.
- giraffe_lady 3y agoNah just a guy who had some teeth knocked out by the state police.
- Anon_Forever 3y ago[dead]
- easeout 3y agoMay I suggest for a logo: A directed graph in the form of a sheriff's star badge
- MobileVet 3y agoNice work!! I have always appreciated the clarity of FSMs since first learning about them in college. They are a great way to think about a system's state and given X input, clearly define the next state. This makes them very easy to test as well.
- pokstad 3y agoLooks cool. It’d be nice if you could output the FSM to some kind of diagram markup (plantuml or mermaidjs).
- hishamk 3y agoThanks for the feedback. I've added a mermaidjs code generator for the ruleset.
- wahnfrieden 3y ago[flagged]
- Solvency 3y agoRust - A memory safe programming language wahnfrieden: Why the iron oxide name? Besides the fact that it's a pun. Is the code written with it always bound to be rusty and old?
- wahnfrieden 3y ago[flagged]
- bsaul 3y agoenums with associated values (like in swift) really are the thing i miss the most in go.
- pjmlp 3y agoYeah, yet another Algol 60 missing feature. I really don't get how one can suggest the iota const dance with a straight face as an alternative.
- rochak 3y agoThe sooner you realise Go is just C with some niceties, the better.
- pjmlp 3y agoWhen are C enumerations coming to Go?
- preseinger 3y agowhat do you think would happen if there was a comment about go on the internet that you somehow didn't see, and to which you weren't able to reply with a sarcastic and sneering dismissal? probably some serious kind of disaster, right?
- pjmlp 3y agoAt crazy as it might seem, I do miss a lot. Then again, too busy reading papers.
- bsaul 3y agoso, i had a look because i wondered if you were joking or not, but algol 60 doesn't seem to have enums (putting it here just in case someone else may read this conversation)
- syngrog66 3y agoA state machine (specifically a FSM) class is something I end up having to reinvent in every new language I've adopted. Such a useful pattern whose need comes up repeatedly. Especially in games/sims or in anything with a GUI. Since I've been making both for decades I have a lot of homegrown FSM classes sitting around. :-p
- samsquire 3y agoI am working on a state machine formulation/notation that can be executed. The runtime is multithreaded and parallel. The idea is to execute the following state machine: thread(s) = fact(variable) | send(message) | receive(message2); thread(r) = fact(variable) | receive(message) | send(message2); This program waits until thread(s) is true in another thread until thread(r) is true, everything left of the equals symbol needs to be true before it passes to the next group of facts after the = symbol. Then it waits for "fact(variable)" to be fired, then when all those atoms are true, the state transitions past the pipe symbol and does a send(message) while the other thread does a receive(message) and then the process is repeated in the opposite direction. I've not shown it here, but you can wait for multiple facts to be true too. Here's a state machine for an (echo) server that is intended to be mapped to epoll or libiouring: accept | { submit_recv! | recv | submit_send } { submit_send! | send | submit_recv } The curly brackets means parallel state machines that run independently, like a fork.
- syngrog66 3y agolove it! shutup and take my money/adoption/usage/attention! ha no but seriously, sounds like a good/useful plan/approach. please let me know if you share out a spec or impl sometime BONUS points for a Golang or C lib
- syntheweave 3y agoThat is a description of flow-based programming, minus the generalization to "arbitrary packets of data in bounded buffers" - that is, your only data type is true/false and your only buffer size is 1, which lets you get some very clean syntax on it. It's a good invention! FBP adapts the physical concept of unit record machines that operate over punchcard stacks, which predisposes it to thinking in terms of processing and transforming richer data like "characters in a string" or "records in an array", but it basically works for truth-signalling logic too - let truth packets wait in a buffer, and then when the node signals that the packets are able to move, that is the transition. The emphasis does differ in that if we are thinking "FSM", the rest of the program and the data it handles have been abstracted, while if we are thinking "FBP" we're engaged with designing specific machines to connect together in terms of I/O, which is more helpful when you have a library of data operations to reuse.
- rowls66 3y agoInteresting. I wrote almost the same code for work a couple of weeks ago. Not sore, but it looks like the Transition function has a race condition. It calls CanTansition() before acquiring the mutex lock. I think this could lead to illegal state transitions.
- jupp0r 3y agoNice job. In my own usage of state machine libraries over the years I have found that for complex use cases, it's definitely helpful to have event-based transition dispatching be part of the library. The great benefit of FSMs is that you can express in data a lot of aspects that you would normally express in code. Not knowing what event needs to happen to transition to one state vs another leaves out a lot of the benefits that you get from designing your code around FSMs. That being said, I appreciate the simplicity and it's a totally fine choice to leave out event based dispatch for less complex use cases! One thing that has been mentioned here already: it's super helpful to have your library output a diagram file to visualize the FSM. This is a really great way to keep code and documentation in sync always.
- hishamk 3y agoThank you. The focus was on simplicity and handling less complex scenarios. In my current projects the business logic lends itself to sitting outside the FSM/State Trooper. The logic dictates the transitions at arms length to the FSM. The FSM does not carry on any particular business logic, it just enforces the transition rules from one state to any number of possible states - hence why I've included the metadata aspect to embed info about the reason for a transition. Re the visualization, I think that would be cool. I might give that a shot.
- naikrovek 3y agoI am actually angry at how good the project name is.
- jnordwick 3y agoI've written 2 in C++ for different projects at companies State Farm and State of Decay. I'm trying to write one that kind of inverts things and calling it Enemy of the State. But yes, his name pisses me off because I didnt think of it either.
- earthboundkid 3y agoA state machine should have more than states. It also needs actions which cause transitions. The API should be about writing out which new state an action causes given a base state. Eg you have a modal with a button and it can be clicked or dismissed. In the open state, click and dismiss cause close, and in the closed state, click causes open. Anyway, the whole thing is only valuable once you have actions.
- packetslave 3y agoCommenters like you are why people don't share thing. Why make something available for free when someone like you is just going to come along and shit on it?
- deleted 3y ago[deleted]
- akira2501 3y agoIf your only reasoning behind releasing an open source project is to receive uncritical accolades just for producing open source, then I think you need to examine your motives and seriously question why you expected this deference in the first place.
- jawr 3y agoCommenters like you are why people don’t give honest feedback. Why review something for free when someone like you is unable to accept honest feedback even if it is critical.
- spicybright 3y agoIf you post your project on a public forum with a comment section, you should expect some people to be critical and suggest things, no matter what it is or how much it costs. Perhaps the real issue is commenters like GP is why YOU don't share things?
- BSEdlMMldESB 3y agoyou mean like: > Add valid transitions between states: > `fsm.AddRule(CustomStateEnumA, CustomStateEnumB)` > `fsm.AddRule(CustomStateEnumB, CustomStateEnumC)`
- whywouldudothat 3y ago[dead]
- klabb3 3y agoI saw the benchmark which seemed crazy (3us per transition and allocations). So looked quickly at the code. Recommend putting the tracking of previous states in a separate optional debugging type so you don’t have to pay the cost in general. Oh and using time stamps as a key is kinda weird, but even weirder in Go where maps are non-deterministically enumerable. Otherwise, seems like a good use of generics, given Golangs particular take on it.
- hishamk 3y agoThanks for the feedback. The benchmark was for six transitions. I've now updated a few things and added new benchmarks. I also got rid of timestamps for the map keys - it's back to a regular slice. In retrospect, that was a tad bit off, I agree.
- preseinger 3y agofunc (fsm *FSM[T]) Transition(...) { ... fsm.Transitions[time.Now()] = Transition[T]{ FromState: *fsm.CurrentState, ToState: targetState, Timestamp: &tn, Metadata: metadata, } does fsm.Transitions grow without bounds?
- TheSwordsman 3y agoThis state machine is inherently unsafe for concurrent use, because the CurrentState and Transitions fields aren't completely protected by a mutex. You already have an unexported mutex that you lock when mutating those fields, but then consumers trying to read those exported fields are not able to lock the same mutex to prevent concurrent read/write operations. You should not export those fields, and instead make them available via methods where the reads are protected by the mutex. You'll probably also need to make a copy of the map since it's a reference type, OR make the accessor take the key name and fetch the value directly from the map and return it. I learned this when I wrote a similar simple state machine in Go ~7 years ago. :) I'd also make sure to return `T` from the CurrentState accessor method and not `*T`, just to make it easier for consumers to do comparison operations with the returned result. Reference on Go memory safety: https://go.dev/ref/mem https://go.dev/ref/mem
- hishamk 3y agoThank you so much for this head's up. I've just pushed out a new update that covers this and a few other changes.
- hmcamp 3y agoThanks for this. I like the simplicity!
- hishamk 3y agoThanks for everyone's feedback. I've pushed out a few changes: 1- Regular slice instead of timestamp-keyed map. That didn't make sense in retrospect. 2- Better benchmarks. 3- Non-exported current state and transitions. Mutexed getters to avoid concurrency issues. 4- Variadic rule parameters. 5- Better example.
- preseinger 3y agoyou define Transition over T comparable, but there is no guarantee that comparable implements json.Marshaler, so FSM's MarshalJSON and UnmarshalJSON don't really work