News for hackers. Stuff that matters.
Re:Gem adds support for creating state machines for attribut... by 38 on Sunday January 21, 2024 @07:08PM (#39084448)
I think Ruby is OK enough, but what is this?

    state_machine :state, initial: :parked do
I think in their zeal to "golf" the language, they forgot to actually keep it readable
Re:Gem adds support for creating state machines for attribut... by aaronbrethorst on Sunday January 21, 2024 @07:14PM (#39084491)
I've been writing Ruby for about 16 years, and I find that eminently readable.

You're apply state_machine to the attribute state, and giving it an initial value of :parked. Then there's a block that ostensibly encapsulates state transitions following (hence the do).

Ruby can be written extremely tersely, and that can be difficult for non-Rubyists to read. This might help:

    state_machine(:state, {:initial => :parked}) do
        # transitions here
    end
What I don't understand is why I'd want use this over aasm (https://github.com/aasm/aasm)
Re:Gem adds support for creating state machines for attribut... by filoeleven on Sunday January 21, 2024 @07:25PM (#39084549)
Positionally-significant colons is more nightmarish than significant whitespace. Speaking as a critic of significant whitespace.

Put them at the front and make them mean something, or put them at the back and make them mean something, Making both places mean something entirely different, or even allowing both as an allowable syntax, is horrible for readability.

Re:Gem adds support for creating state machines for attribut... by softjobs on Sunday January 21, 2024 @07:32PM (#39084611)
The colon sigil denotes a Symbol class literal, and a trailing colon here denotes a Symbol used as a Hash object key.
Re:Gem adds support for creating state machines for attribut... by rco8786 on Sunday January 21, 2024 @07:57PM (#39084795)
Ruby uses symbols (strings prefixed with a semicolon) very liberally. It’s just a value literal. Ive never met anyone who writes Ruby that has an issue with the readability of it.
Re:Gem adds support for creating state machines for attribut... by leetrout on Sunday January 21, 2024 @08:22PM (#39084953)
Selection bias
Re:Gem adds support for creating state machines for attribut... by fnordpiglet on Sunday January 21, 2024 @08:57PM (#39085173)
I’m a Perl guy, and I find Ruby bizarrely verbose. I don’t understand how people can enjoy their symbols with all those word things.
Re:Gem adds support for creating state machines for attribut... by donw on Sunday January 21, 2024 @10:38PM (#39085907)
Right? If it doesn't use the shift key and the entire numeric row of the keyboard, count me out.
Re:Gem adds support for creating state machines for attribut... by rco8786 on Monday January 22, 2024 @06:52AM (#39088496)
Would disagree. I write in other languages and I find things tough to read. There are things about ruby I find tough to read (like `unless`) and know many other people who agree.

Symbols work just fine in practice.

Re:Gem adds support for creating state machines for attribut... by 38 on Monday January 22, 2024 @05:37PM (#39096513)
Re:Gem adds support for creating state machines for attribut... by rco8786 on Tuesday January 23, 2024 @12:55PM (#39106928)
Is it stockholm syndrome if I leave ruby and years later still wish I had symbols in other languages?
Re:Gem adds support for creating state machines for attribut... by vidarh on Sunday January 21, 2024 @10:37PM (#39085905)
They mean "symbol" either way, except when trailing ":" also means they're a key.

I'd be inclined to agree it's a wart, but not that it affects readability to any noticeable amount.

In this case, though, the DSL looks just awful to me.

Re:Gem adds support for creating state machines for attribut... by lloeki on Monday January 22, 2024 @03:43AM (#39087579)
To expand on this:

Contrary to constants which are fixed+ references to values, symbols are pure values: they're like integers but with a textual name. Explicitly they are not strings.

They used to be very useful because a symbol takes little memory and comparing two symbols means comparing two integers; it's a bit like `#define FOO 42` in C, except `42` is automatically picked up and you don't care what the value actually is.

They are less useful today because there have been a lot of improvements e.g with frozen string literals (which are now stored only once and thus can be compared much more efficiently). Still that has a lot to do as to why they're used for kwargs and hash keys and method names and a ton of other stuff, where without symbols you'd have a ton of the same strings showing up over and over, so instantiated and compared.

The hash syntax used to be only arrow but it was so common to do `:foo => "bar"` that at some point Ruby (1.9) introduced `foo: "bar"`, which makes writing hashes look like JSON/YAML and kwargs more like other languages like Python. This does make it a bit surprising because suddenly there's a symbol that is not prefixed with `:` but it also makes things a ton more readable in many cases.

I do use the "new hash syntax" liberally, but also still use arrow hashes a lot because it all depends on the way things are written around and sometimes one is more readable than the other.

For example in general I don't like writing rake task target dependencies or Rails routes with the new syntax, the arrow one makes more sense to me. Or when one can have mixed keys that can be symbols or some other types, I find it more readable to have a consistent hash syntax, so I go to the old arrow one.

+ well, although frowned upon, constants can be reassigned, they're essentially just some variables albeit with a different scope and resolution system.

Re:Gem adds support for creating state machines for attribut... by pdntspa on Sunday January 21, 2024 @11:08PM (#39086079)
It seems like you have the concept down pat. I don't see what's wrong with it?

Like lots of languages use positionally-dependent symbols. In both Perl and VB various characters in the variable name implied things about its type. With C/C++ the position of * tells you lots of important things. How is this anything less than yet another random language quirk?

Re:Gem adds support for creating state machines for attribut... by nerdponx on Sunday January 21, 2024 @07:31PM (#39084604)
Wait, is :state a keyword or an instance attribute? Using the same syntax for both seems really confusing.
Re:Gem adds support for creating state machines for attribut... by deschutes on Sunday January 21, 2024 @08:50PM (#39085135)
It's a symbol. Basically a glorified string literal.
Re:Gem adds support for creating state machines for attribut... by lloeki on Monday January 22, 2024 @03:20AM (#39087472)
It's more like an integer with a textual name.
Re:Gem adds support for creating state machines for attribut... by vidarh on Sunday January 21, 2024 @10:32PM (#39085885)
Ruby doesn't have "instance attributes". Instance variables are prefixed with "@". Anything prefixed by ":" is a symbol.

In some contexts we use symbols to refer to methods by name.

Anything you might thing is an "attribute" is likely a method. E.g. on some_object.name, name is always a method. You might be confused because e.g. "attr_reader" takes symbol arguments, because it's just another method. E.g. "attr_reader :foo" defines a method named "foo" to return "@foo". ":foo" there is a Symbol representing the name "foo".

Re:Gem adds support for creating state machines for attribut... by nerdponx on Monday January 22, 2024 @09:02AM (#39089622)
So it's just a symbol, but in this context it will be used to look up the "state" method? Not unlike how symbols are sometimes used as "designators" of various things in Lisp?
Re:Gem adds support for creating state machines for attribut... by qudat on Sunday January 21, 2024 @08:23PM (#39084960)
I find it way easier to read this modified version — and I use ruby at work. It’s not even close.
Re:Gem adds support for creating state machines for attribut... by saghm on Sunday January 21, 2024 @10:53PM (#39085997)
I think the choice to "name" all arguments (`from:`, `to:`, which if I recall from my bygone Ruby days is implicitly parsed as a single hash argument) goes a long way to make things more consistent compared to having the first argument passed separately, making the colons a lot harder to parse visually due to the inconsistency.
Re:Gem adds support for creating state machines for attribut... by cryo28 on Sunday January 21, 2024 @08:47PM (#39085122)
First of all these are not new gems. In the past I used both and I strongly prefer this one to aasm. I like the DSL of it. It seems more convenient to me.
Re:Gem adds support for creating state machines for attribut... by vidarh on Sunday January 21, 2024 @11:02PM (#39086048)
Both of these gems just feel unreasonable verbose to me. E.g. why "transitions from: :somestate, to: :otherstate" vs just "transition :somestate => : otherstate"?

A lot of the syntax of these feels over-engineered, or like someone has just read a Smalltalk introduction and is overdoing keyword arguments to emulate it.

If I'm going to use a DSL to define a state machine, it better be a lot terser and/or clearer than a method with a case block.

E.g.

    det state = @state || :initial_statr

    def state=(event)
      case [state, event]
      in [:somestate, :someevent]
        someaction
        @state = :newstate
      in ...
      ...
      end
    end
or case event ..., or case state ..., depending on the "shape" of your state machine.

("in" vs "when" here largely depends on whether you want to use any of the newer, fancier pattern matching allowed with "in"; I'm not sure it matters here)

I could see wrapping up those two methods in a single small helper, and maybe a little bit extra sugar to make it easier to decompose large state machines, but it better be a complex set if transitions before I'd not throw a fit over the use of these gems in a code review.

(Yes, I can see they offer a lot more information about the states and transitions, but I'd argue they offer far too broad APIs for it by default, and you'll end up going hunting for method definitions that don't exist all over the place because of all the methods it injects)

In particular the whole before/around/after transition screams at me to apply caution to prevent people having to hunt around to figure out the total set of possible effects of a transition.

Re:Gem adds support for creating state machines for attribut... by mp1mp2mp3 on Sunday January 21, 2024 @11:31PM (#39086219)
On why not to use "transition :somestate => :otherstate", it would be because then other optional arguments might conflict with the state names. For example, if the `transition` method takes an optional argument of `given:` then a state machine could not have state called `given`. In fact, no optional arguments could be added in a backwards compatible way.

The states could be passed positionally but, imo, the increased verbosity helps readability rather than hinders it.

Your example code seems not really thought through. If I were to implement that, then I would write code that looked like this: `my_thing.state = :some_action; assert my_thing.state == :some_new_state`. Now we're spending company money discussing whether the state machine code you decided to write by yourself has a reasonable interface.

I've used aasm for years and it's just fine. If someone suggested we just implement a non-trivial state machine with our own homegrown solution, I would push back. Trust the community's experience and just pull in the well-established library. There seems to me a certain hubris in deciding that the rest of the world has settled on an overly complex solution when, if they had just thought for a moment, it should just be a 'switch` statement.

Re:Gem adds support for creating state machines for attribut... by vidarh on Sunday January 21, 2024 @11:50PM (#39086347)
Then you explicitly pass the transitions as a hash, or provide other methods for those specific options, but part of my point was that adding so many options in the first place is part of the problem of that API. It's trying to do way too much at the same time, and the result is you're ending up with really excessive code.

My example code was a quick and dirty example; instead of state= you could use "event" or whatever name you prefer. You're having the discussion about API whether you have it explicitly or have it by having the discussion about pulling in those gems.

And the point was the verbosity of it, not the details. If you need "a solution" for a state machine, odds are your state machine is too big and convoluted and ought to be decomposed, because to me at least their examples obfuscates the state machine they're defining in a way that is a huge red flag.

I definitely would not trust "the community's" experience on this - it's by no means "the rest of the world" that has settled on this, but a tiny subset. Thankfully I've never had to deal with code using either of these gems "in the wild" in 19 years of Ruby development.

Having looked at the source of the state_machine gem, I'm now even less inclined to want it anywhere near my code. It's grossly over-engineered. A quick look at aasm makes it look slightly saner, but it's still grossly invasive and huge amounts of code.

Re:Gem adds support for creating state machines for attribut... by vidarh on Monday January 22, 2024 @02:15AM (#39087141)
Too late to edit, but see also: https://news.ycombinator.com/item?id=39087098 where I give a more fleshed example of something more similar that I'd be happier with, and which you'll notice is a lot closer to aasm than the linked gem. Compared to the state_machines gem, aasm I have mostly nitpicks over the syntax of and most of my issue is that the implementation is way more complex than it needs to be.

By all means, use what you're familiar with - aasm is by far the better choice if you've first made the investment.

Re:Gem adds support for creating state machines for attribut... by gedanziger on Monday January 22, 2024 @03:03AM (#39087383)
I worked on a state machine framework in another language, and have definitely have found less terse to be pretty good. Typing a few extra characters isn't that bad, especially if it makes some awful bit of evented code easier for someone to understand.

Of the things available open source, I think P-lang is pretty cool: https://github.com/p-org/P/blob/master/Tutorial/1_ClientServ...

Re:Gem adds support for creating state machines for attribut... by vidarh on Monday January 22, 2024 @03:53AM (#39087618)
There are thresholds in both directions, and I find the state_machines examples go way too far in the direction of verbosity to the point where it makes it harder rather than easier to see what is going on.

I'd argue the same for your P-lang example.

The state transitions in your example are trivial to the point where a "framework" is entirely unnecessary, but even then they are hidden inside the implementation details in a way that forced me to read the implementation to realise that they're trivial.

If I have a state machine that is complex enough that I want a "framework" to simplify things, my first requirement will be that it makes clear the state transitions and criteria separate from the implementation of them. Both the state_machines gem and the other Ruby gem mentioned in this thread have examples that in my opinion does a far better job at separating that.

Re:Gem adds support for creating state machines for attribut... by jrvarela56 on Sunday January 21, 2024 @07:52PM (#39084763)
Readability is one of Ruby's strengths. Most custom DSLs I've encountered are easy to understand. Arguably meta programing makes it harder but the language allows you to strike the balance that suits you.
Re:Gem adds support for creating state machines for attribut... by 38 on Monday January 22, 2024 @05:38PM (#39096522)
good joke
Re:Gem adds support for creating state machines for attribut... by bradly on Sunday January 21, 2024 @07:44PM (#39084698)
I don't think this is bad, but my preference would be to change `initial` to `initially`. I'd probably also make :state the default name as well, but that may be too magical for some, especially those not used to Rail's omakase philosophy.
Re:Gem adds support for creating state machines for attribut... by dymk on Sunday January 21, 2024 @07:44PM (#39084697)
This is idiomatic Ruby syntax, and anyone familiar with the language knows what that code does
Re:Gem adds support for creating state machines for attribut... by heleninboodler on Monday January 22, 2024 @02:37AM (#39087262)
This is kind of an absurd statement, given how prevalent custom DSLs are in Ruby (and in this example). No amount of familiarity with the Ruby language can tell me what this means:

       event :ignite do
         transition stalled: same, parked: :idling
       end
You have to be familiar with the DSL, not Ruby.
Re:Gem adds support for creating state machines for attribut... by dymk on Monday January 22, 2024 @07:03PM (#39097394)
It's generally good practice to read a library's documentation to understand how to use it - there's not really a short cut to that, regardless of the language.

As for the syntax, you know that `event` is a method, passed the param `:ignite` and a block, and in that block, you call the method `transition` with kwargs `{stalled: same, parked: :idling}`, and `same` is another method call.

Re:Gem adds support for creating state machines for attribut... by heleninboodler on Tuesday January 23, 2024 @02:09PM (#39108080)
Indeed I know all of this. But in terms of "what this code does," the information that a method is passed a block that calls another method is not particularly interesting. The major problem I have with this sort of DSL is it encourages people to infer what it does based on what the ad hoc method names make it sound like this is intended to do. So many of these custom DSLs are documented by example rather than by any decent specification. The attitude that these clever little grammars make it so you can "just read it and it's obvious" is, in my opinion, sloppiness in code comprehension taken to an extreme.
Re:Gem adds support for creating state machines for attribut... by __s on Sunday January 21, 2024 @07:21PM (#39084528)
It can come as "golf" language, but those features are used to achieve DSL capabilities

See how much mileage Lua gets by `f "a"` be `f("a")` or `f{a = 3}` be `f({a = 3})`. Haskell achieves the same

Re:Gem adds support for creating state machines for attribut... by rco8786 on Sunday January 21, 2024 @07:56PM (#39084784)
This is pretty standard Ruby stuff. It’s different for sure. But works really nicely in practice.
Re:Gem adds support for creating state machines for attribut... by akerl_ on Sunday January 21, 2024 @08:53PM (#39085151)
It’s a function call, with a positional arg and a keyword arg?
Re:Gem adds support for creating state machines for attribut... by kshahkshah on Sunday January 21, 2024 @09:36PM (#39085463)
everyone in this chain is forgetting to mention a core design principal of Ruby.

Matz wants a principal of least surprise. That doesn't mean _your_ least surprise as a non-Ruby programmer, or someone with passing familiarity with the language. It means the least surprise of someone who has worked with the language for years. What Matz has said is that C++ continues to surprise him years and years in, and he _didn't_ want that to be the case for Ruby.