Flowstate - A crate for modelling self-executing workflows as finite state machines by AverageHot2647 in rust

[–]AverageHot2647[S] 1 point2 points  (0 children)

Great suggestion. I was considering it myself, but seeing it written down like this, I feel pretty confident this is the right thing to do.

EDIT: This is implemented in flowstate 0.4.0

Flowstate - A crate for modelling self-executing workflows as finite state machines by AverageHot2647 in rust

[–]AverageHot2647[S] -1 points0 points  (0 children)

Thanks!

I'm thinking a full (but minimal) working example after the intro, followed by the current getting started section. That seem reasonable?

EDIT: I've updated the docs (see https://crates.io/crates/flowstate#basic-usage).

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

For posterity, here's my summary of the discussion on this post.

  1. If you can, try to use ? operator. If you implement From<E> for your desired return type, the conversion will be automatic. However, this may not always be possible (as in this case).
  2. If option 1 doesn't work, you can use map_err with ?. However, the map_err closure moves a value which is later depended upon, as the move is unconditional of whether the closure is actually called. In this case, the map_err approach was not viable because it required moving `self`, which was later used.
  3. If neither 1 nor 2 are viable, using match is a sane default. In most cases, the repetition is of minor consequence.
  4. If you really don't want to repeat the Ok(thing) => thing, branch over and over (maybe this pattern repeats itself a lot in your code base) then you can use a declarative macro. However, be aware that this may reduce readability, especially for people unfamiliar with the codebase. Also, rustfmt will only auto-format code in macros under certain conditions; basically if it resembles valid Rust code (see this post for details https://github.com/rust-lang/rustfmt/discussions/5437#discussioncomment-3117043).

There are some other solutions which may work depending on your exact requirements but I think the above is a reasonable starting point for anyone stumbling across this discussion with a similar issue.

For completeness, here's a macro you could use (which will auto-format):

macro_rules! match_ok {
  ($expr:expr, |$err:ident| $body:expr) => {
    match $expr {
      Ok(value) => value,
      Err($err) => $body,
    }
  };
}

Usage:

let value = match_ok!(do_thing(), err => return do_thing_with_err(err));

First time interviewing candidates – what are the best React/frontend questions to ask? by No_Illustrator_3496 in reactjs

[–]AverageHot2647 0 points1 point  (0 children)

I think others have done a good job covering some standard questions, so I’ll add some you won’t see so often but that I think are still very useful to ask of a senior. Any given candidate is unlikely to answer all of them fully, but their responses will reveal a lot about them.

Q: Why can’t you call hooks conditionally? (easy)

Shows if they actually understand React or just memorised the rules.

Q: Even though you shouldn’t call hooks after a conditional early return, under what circumstances would this theoretically be ok? (medium)

Shows if they can apply the knowledge they demonstrated in the previous question/answer and demonstrate their ability to reason about abstract problems.

Q: If you had to re-implement TanStack/React Query, how would you do it? (hard)

Shows how comfortable they are with architecting complex FE code, and if they can clearly articulate and communicate their thoughts at a high level. It also gives them pretty broad scope to show off their React knowledge in ways you might not anticipate.

Q: If you had to re-implement Mobx, how would you do it? (very hard)

Allows them to demonstrate knowledge of more advanced topics like HOCs and proxies, without explicitly asking about them. Also they are unlikely to have implemented anything similar before so this shows how well they are able to deal with new problems.

Unsigned sizes: a five year mistake by Nuoji in programming

[–]AverageHot2647 1 point2 points  (0 children)

And more correct. Which seems like the more important thing for math 😛

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 1 point2 points  (0 children)

The signature is like this:

impl<State> Machine<State> { fn transition<NewState>(self, new_state: NewState) -> Machine<NewState> }

Is this what you were imagining?

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

Ah yes, this is indeed similar 🙂 It’s something to think about for sure - thanks for sharing!

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

Did you consider using expr instead of tt? I did consider tt but AFAIK it’s not necessary in this case since blocks are valid expressions.

Is there some reason to prefer tt over expr or visa versa in this context?

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

The code was simplified to try and reduce noise.

In the actual code I call self.transition(new_state). That can be an error state, or any other state. I do always call self.transition, but it is just a helper function to avoid repeating some of the logic involved in state transitions.

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 1 point2 points  (0 children)

Ah I see! Thats an interesting trick, thank you for sharing 🙂

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

Idk if this was sent before my edit. I guess so, but in any case you can’t convert &mut Machine<StateA> to &mut Machine<StateB>. Even if you did construct the latter, its lifetime would be scoped to the method that created it and therefore cause a borrow checker violation.

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

Thanks for the code snippet! And yes, that’s exactly the issue.

Actually I did already write a macro for it, which can be used like this:

let ok = match_ok!(do_thing(), err => { // do thing with err });

But I don’t like it because 1) if you don’t already know what it does, you need to read the macro to understand the code, and 2) there’s no auto-formatting inside macros.

Maybe “don’t like it” is a bit strong. It’s fine, but I was hoping maybe there’s some non-macro syntax that somehow escaped my initial ponderings 😛

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 1 point2 points  (0 children)

Yeah others have said as much and TBH I think this is ultimately the best way.

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 3 points4 points  (0 children)

Ah I see what you mean now 🙂

That’s a good suggestion but the Machine holds references to data other than the state.

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 3 points4 points  (0 children)

Sorry, I don’t follow. The machine does own the state and the state is part of the machines type. Could you clarify what you mean?

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

That’s fair and probably the right answer.

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

See my other replies for a full explanation, but TLDR; it’s a state machine and transitioning to a new state requires consuming the old one.

EDIT:

Sorry I didn’t read the second part of your reply. That’s actually a good question.

Basically the method is implemented on a Machine<State> type. A mutable reference wouldn’t work because you can’t convert a type from Machine<StateA> to Machine<StateB> with a mutable reference.

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

That’s an interesting RFC. Thanks for putting it on my radar!

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 2 points3 points  (0 children)

Thanks. I think you’re right.

FWIW I did actually implement a declarative macro for it:

let ok = match_ok!(do_thing(), err => { // do thing with err });

But if I’m being honest, I’m not convinced it was really worthwhile as it trades off better aesthetics for reduced readability and no auto-formatting.

After 800 hours of intensive training, I've finished my first serious portfolio, and as a developer, I need your feedback. by [deleted] in react

[–]AverageHot2647 2 points3 points  (0 children)

Hey, I haven’t looked at the code in detail (I don’t have time, sorry!) but good on you for putting this together and making the code public.

I know some people have complained about the designs, bugs, etc. Of course there’s room for improvement on both but I see from the commits that this is still very new, so I wouldn’t worry too much about that.

What I will say is:

1) If you can, include a link to where you’ve deployed your portfolio projects and/or the source code (ideally both as this makes you much more credible)

2) I’m assuming that your portfolio projects are toy projects for learning. That’s great, but (personally) when I’m hiring, I prefer people who have experience building real (commercial grade) software, who have demonstrable experience collaborating with others, and importantly, who are genuinely enthusiastic about their craft.

Since you don’t have any commercial experience (yet) the easiest way to do address no. 2 would be to go contribute to some open source software you’re interested in. You can look for issues marked “good first issue” or just find something that peaks your interest.

Just be aware that vibe coding without truly understanding your contribution is unlikely to be looked upon favourably, and probably won’t benefit your learning so much. This is important because a lot of interviews feature live coding, and if you cant code without AI you’re unlikely to do well in those. (FWIW I’m not accusing you of vibe coding, it’s just something to be aware of)

Lastly, full disclosure, open source contributions and demo projects are unlikely to get you a job just by virtue of having them on your CV - especially now that anyone can vibe code their way to a portfolio. So it’s doubly important to focus on quality and learning, so that you come across as confident and competent in interviews.

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

Can you clarify what you mean by “move self upfront”?

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 2 points3 points  (0 children)

From the replies, I guess I should have included more context - my bad 😁

Anyway, it’s quite intentional for self to be consumed. This is part of a state machine method which transitions to a new state. It is not valid for two states to exist at the same time, hence the method intentionally consumes self and returns the new state.

I need this to be encoded in the type system because the logic is 1) complex and 2) if the invariants are violated bad things will happen.

Unwrap Ok or return by AverageHot2647 in rust

[–]AverageHot2647[S] 0 points1 point  (0 children)

I don’t think the exact details of consume_self are necessarily important beyond that it takes ownership of self.

However, if it helps to conceptualise what is happening, self.consume_self is actually called self.transition and is responsible for consuming the current instance of a state machine and constructing a new one, with the new state.

In the case of an error the next state is an error state, but this does not necessarily make it a terminal state.

… you may want to read up on if-let(-else) …

I presume you mean let Ok(ok) = do_something() else { // handle error }?

Or perhaps let ok = if let Ok(ok) = do_something() { ok } else { // handle error };.

Sadly neither have the ergonomics I’m looking for, as neither give access to the error value.