-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Types Roadmap #61
Comments
@cassiozen I want to provide a safe version in v1.0.0 itself. Which one of the two options you like? And what should we name it? "safe" is very ambiguous. |
What if we flip? We make "safe" the default version and create an "edge" version form more experienced/adventurous users? |
We need nested states - hierarchical/recursive states are the next big feature I want to add. |
We could do that but the problem is that these features to the users are not "edge" but really basic and beginner-friendly, for TypeScript it's "edge". What I mean when I say "really basic and beginner-friendly"? Example - useStateMachine({
schema: { events: { X: t<{ a: number }>(), Y: t<{ a: number }>(), Z: t<{ b: number }>() } },
initial: "a",
states: {
a: { on: { X: "c" } },
b: { on: { Z: "a" } },
c: {
entry: ({ event }) => {
let x = event.a
// event here is narrowed down to { type: "X", a: number } | { type: "Y", a: number }
}
}
},
on: { Y: "c" }
}) It's only because of txstate-like types we can do that narrowing, with the "safe" version beginners would go "Umm why does it not allow accessing property So my suggestion would be to not flip it. |
Yep sure. The "more?" was only for more linter rules haha. Will add another top-level more xD Also I'm thinking of this roadmap to only include typelevel features meaning once we have runtime hierarchical states it's obvious the types will have to support that. So it's not a "type-level" feature but rather a "runtime-level" feature |
@davidkpiano @Andarist Yall should keep an eye on what I'm doing here :P because all these things would eventually come to txstate too ;) |
I think these lint rules already go a long way. I can't think of any others. The "more" that I referred to was for the 1.2.0 release (or later) 😁
Very true. The types need to support that, and I'll work on the "runtime" code. |
Regarding the "safe" version: I'm having an internal debate about it. My initial thought is that it would be confusing for a beginner to understand why a "safe" version exists. As an exercise: What's the worst that can happen? A new version of TypeScript breaks useStateMachine? |
Yep that. Or to be precise, the users using useStateMachine would have their builds failing when they upgrade TypeScript. And the worst is this happens and we don't have any workarounds to commit and publish a new version of useStateMachine. In that case the users would have to switch to safe version (and maybe we'll have to make edge same as safe and publish) and the existing code will have to be edited with assertions. We could provide a few helpers that make it a little easier. So they'd have to do something like... effect: ({ event }) => {
let x = (event as XEvent | YEvent).a
} Or with helpers... effect: ({ event }) => {
assertEventType(event, ["X", "Y"])
let x = event.a
} In theory one could write a codemod for this but would be quite a task :P Edit: Rethinking about the codemod thing - I think it could be actually be a thing we can provide that edits your edge code to make you able to degrade to safe, in that case users would use edge version with confidence as they'd know the codemod has their back. Wdyt? Is this something worth exploring? |
Let's go with the safe version then. Let's call it "boring"? |
Ahahaha xD well... It's kinda better than safe at least, but sounds way too informal to me. Maybe "lite-typed" or something like that? Other options that come to mind (will keep editing): imprecise-types, imprecisely-typed, meagerly-typed, degraded-types, safe-types, hackfree-types |
I think "light-typed" is the best we could come up with |
Sounds good. We can always change it last minute if we come up with something better ;) How about lightly-typed tho? light-typed feel grammatically incorrect to say. Or even light-types would work. And how about "lite" instead of "light", I think the former is more popular? (mobx-react-lite, caniuse-lite come to mind) |
I don't really care if it's "lite" or "light" - both works for me. lightly-typed sounds good. |
I think I like "lite-types" more. Wanna go with that or "lightly-typed"? I don't have a strong preference tho, just lightly-typed has more syllables :P But then I think lite-types in not an adjective argh xD idk anymore whatever works for me |
Final decision: lite-types I like that it's short and I don't care whether it's grammatically correct or not. |
Done, it's good! |
let [state, send] = useStateMachine({
schema: {
events: {
A: t<{}>()
}
},
initial: "a",
states: { a: {} }
})
send("A") Should we allow this? The user has defined an event in schema but it uses it nowhere in the definition. Or this should be a lint rule? It could be they might have kept it for later so an error seems too restrictive to me, not sure tho. |
It should be consistent with what happens if the user adds a context schema before adding the context initial value in the definition. Right now it's an error (if I'm not mistaken). |
It does give an error right now but I don't see how the comparison is 1:1. But going with that and even generally it looks like a mistake which should give an error. Later we could add an |
Yeah, agreed |
@devanshj how hard/quick would it be to make the state definition recursive in the type level? I have a decent idea of how I would implement it at "runtime" level - and I'm starting to play with the idea of including it on the final 1.0 release. |
It's definitely doable but to answer your question you'd have to tell me what the runtime behavior is. Basically what happens when the machine in node In case of xstate, in nutshell (nutshell being the keyword here :P) this happens -
(It's hilarious and ironic that I haven't used xstate even once, have written zero machines with it yet I know the spec quite well because of txstate 🤣) If the behavior going to be same as this, well then it might take some considerable time and effort. Especially writing the tests, implementation might be written quicker. And if the behavior is going to be simpler then might take less time and effort. Also, I'm not an expert by any means nor do I have strong opinions about it but be careful when not following the spec because my guess is it's been developed by many heuristics that might not be obvious right off the bat. You might also want to check what Steve does with state-designer or even other libraries for that matter. So yeah the question is this: what happens when the machine in node |
Yes, absolutely. When I say we don't adhere to the spec is not out of lacking knowledge or will, it's a conscious decision. My impression is that the scxml spec (assuming that's the spec we're talking about 😝) is too broad - in certain cases (like actions), it gives many ways to achieve the same thing. And that's totally fine, nothing wrong with this concept, but it does imply that a library that fully follows the spec will necessarily have a bigger footprint (and bigger docs, etc).
Oh, I've been following state-designer, Robot, react-states and others for a long time. My initial experience with State Machines was with Ruby on Rails back in the day, and I also had a fairly popular state machine library for ActionScript many years ago.
What you described is correct. The library will check if the current state node or any of its parents listens to the "X" event (meaning they have "X" in their "ON" definition). Let's say we have this: {
initial: 'A',
states: {
A: {
initial: 'A1',
states: {
A1: {
initial: 'A12',
states: {
A12: {
},
},
on: {
GOTOA2: 'A2',
},
},
A2: {},
},
},
B: {},
},
} Initial state is A.A1.A12 If I send ("GOTOA2), it will check recursively if the states take this event: A12? No. A1? Yes. So the transition will happen. Exit A12 |
No problem, we can do this on a later release. |
Yeah my impression of the spec is similar and I never meant following the spec exactly - what I meant was let's not do something too out of the way and we aren't as you said the transition behavior is going to be same. And anyways you're more expert at this than me so no worries about that :P Cool then we'll keep this for a later release, so this will be my next task after I'm done with |
Provide a hack-free safe degraded version of types. Because the currect depend on some likely typescript non-guarantees that might break, So if some users would like to trade safety over precise types and developer experience they can use that version. And we can also be less worried of making workarounds because we have already warned "Hey this might break" :P
Two ways of going about this -
@cassionzen/usestatemachine/safe
(temporary name)Current workarounds -
InferNarrowestObject
which is almost same as this andDefinition.FromTypeParameter
(TODO: document what's happening here)For milestone v1.0.0
Support guard's that are actually typed as type guards. Meaning this should work -
It's great the signature is
({ context, event }) => ...
instead of(context, event) => ...
like xstate, because latter is principally incorrect and consequently has some consFor milestone v1.1.0
Provide (probably opt-in) linting
noInitialToDeadStateNodes
noNonExhaustiveEventsSchema
-noSchemalessDefintion
For milestone v1.2.0
more?
The text was updated successfully, but these errors were encountered: