Git Product home page Git Product logo

Comments (18)

matthewp avatar matthewp commented on September 24, 2024 4

That works for me.

Using this issue as a cross-repo tracker:

  • - Add symbols to lucy-prism
  • - Add symbols to lucy-vscode
  • - Update docs to reflect preference for symbol usage and update compilation examples.
  • - Add symbols in liblucy and make sure they work in: (PR: #41)
    • - assign(prop, :load)
    • - action(:log)
    • - guard(:check)
    • - delay(:dynamic)
    • - invoke(:callback)
    • - spawn(:machine)
    • - action logger = :log
    • - guard check = :check
  • - Make invoke callable in liblucy
  • - Pass context through the new exported fn

from liblucy.

mattpocock avatar mattpocock commented on September 24, 2024 3

@matthewp Very nice. I dig that :something syntax, too. Very clear and should allow for nice autocomplete for duplicates.

One small change I would make is to change:

export default function(context, options) {}

to...

export default function(options) {}

Where context is a key on the 'options' object. This more closely matches current implementations, like useMachine, and also covers spots where implementations are required but context is not. For instance, context-less machines like Tab Focus here:

https://xstate-catalogue.com/machines/tab-focus

from liblucy.

matthewp avatar matthewp commented on September 24, 2024 1

Thanks for raising this issue, I had thought about some sort of function export initially, I was thinking about it in terms of unimplemented actions/guards like you have mentioned. So I was concerned that there might need to be some type of export syntax and a way to define arguments. Now I see that we probably don't need that. Any unimplemented guard/action becomes part of the arguments.

There's probably a lot to discuss here, so. I want to think/talk about this more so we can break down the issue. I would say that the ultimate goal here is to allow completely implementation-free Lucy machine definitions.

Possibly even deprecating/removing implementation touch points like use and inline actions/guards. Ergonomics is key here, as inline actions/guards were pretty ergonomic and we don't want to lose that by getting rid of them.

from liblucy.

matthewp avatar matthewp commented on September 24, 2024 1

Side-note, this would also fix the fact that you currently cannot define the initial context in Lucy (except clumsily through @entry definitions in your initial state).

from liblucy.

mattpocock avatar mattpocock commented on September 24, 2024 1

Side-note, this would also fix the fact that you currently cannot define the initial context in Lucy (except clumsily through @entry definitions in your initial state).

Currently, you could do it via machine.withContext(). But point taken, a function would be much more ergonomic.

use is a really interesting API, because it allows for arbitrary code execution while still allowing the lucy schema to be statically extractable. But typing it might be more trouble than it's worth? Torn.

from liblucy.

matthewp avatar matthewp commented on September 24, 2024 1

I think where I'm landing on this issue is:

  1. Change exported machine defintions to be a function that creates the machine. This will make TypeScript easier (future issue).
  2. Use a convention of create[MachineName] as the exported function name. Single machine Lucy files will continue to export default.
  3. Add new syntax: :name to signal an external value provided by initialization.

use and referencing imported JS will remain in, for now. We can omit showing that in the docs, and assuming all goes well we can deprecate and remove in the future.

Other things discussed in this issue (such as special syntax for data properties) will be done in a future issue.


Example payment machine:

initial state idle {
  purchase => purchasing
}

state purchasing {
  => guard(:fundsAvailable) => action(:logTransaction) => purchased
}

final state purchased {}

Would produce:

import { createMachine } from 'xstate';

export default function(context, options) {
  return createMachine({
    initial: 'idle',
    context,
    states: {
      idle: {
        on: {
          purchase: 'purchasing'
        }
      },
      purchasing: {
        always: [
          {
            target: 'purchased',
            cond: 'fundsAvailable',
            actions: ['logTransaction']
          }
        ]
      },
      purchased: {
        type: 'final'
      }
    }
  }, {
    ...options
  });
}

from liblucy.

matthewp avatar matthewp commented on September 24, 2024 1

@mattpocock You mentioned passing context through as part of the options. I think that will mean the signature will look something like this:

export default function({ actions, assigns, context, delays, guards, services }) {
  return createMachine({
    context,
    // ...
  });
}

My TS skills are a little weak, will we be able to type context here using generics?

function createToggleMachine<T>({ actions, context }: { actions: SomeType, context: T }) {

Is that valid? Would it be valid if the options type here was moved up into an interface as well?

interface CreateToggleMachineOptions<T> {
  actions: SomeType;
  context: T;
}

function createToggleMachine<T>({ actions, context }: CreateToggleMachineOptions<T>) {

from liblucy.

matthewp avatar matthewp commented on September 24, 2024 1

This is close to being ready implementation wise. Biggest issue at the moment is what to do when invoking/spawning submachines. How are the child machines going to get their options dependencies? Currently I think we are going to have to say that child machines can't have options deps and we'll just call the createChildMachine() function to get the StateMachine instance. Down the line we'll need a solution for this though.

from liblucy.

matthewp avatar matthewp commented on September 24, 2024 1

Code is implemented. All is left is updating docs. PR is here: #41

If anyone wants to review, this is the test to look at:

Does this match everyone's expectations? What I like about this is that you can use symbols anywhere, in spawn(), in invoke(), not just action/guard.

from liblucy.

matthewp avatar matthewp commented on September 24, 2024

Some things we need to figure out:

  • What named invokes look like.
  • The naming convention for exported machine functions (ex. does ToggleMachine become createToggleMachine? Is that intuitive for the user?)
    • I've also wanted it to be possible to have machine definitions that are not exported. How does that work?
  • What the arguments should be.
  • If there's a more ergonomic way to define named actions/guards than the current form.

from liblucy.

mattpocock avatar mattpocock commented on September 24, 2024

https://www.loom.com/share/8f652e019a9b4eada5ca9662b13cec8e

from liblucy.

matthewp avatar matthewp commented on September 24, 2024

Nice! Yeah we might be able to just change all of these to function-call style, where the argument is actually the name. Something to think more about, want to look at other DSLs for inspiration here as well.

By non-exported machine definitions I mean something like this:

machine uploadMachine {
  // ...
}

machine MainMachine {
  state startup {
    invoke uploadMachine {
      done => idle
    }
  }

  state idle {}
}

uploadMachine is used internally only. Currently it will be exported though. Ideally we would only export the machines intended to be interpreted. In Go the first letter determines if it is exported; if it starts with a capital letter it is, if it starts with a lowercase it is not. Possible we could use that convention too. I'd prefer avoiding export keyword if possible (don't think DSLs typically have such a concept).

from liblucy.

matthewp avatar matthewp commented on September 24, 2024

Types of references in Lucy:

  • Identifiers - References to variables inside file
  • Data - Context properties (idea -> @todo)
  • Implementation names - Named actions/guards/delays, etc. (idea -> :canProceed)
  • Event names - Names of events

https://www.loom.com/share/2d4297e26e014f22ac218a8faa0a4b23

cc @mattpocock

from liblucy.

mattpocock avatar mattpocock commented on September 24, 2024

@matthewp Nice!

Agree with everything in here. One thing to note is a fifth kind of 'thing' in Lucy - spawned actors in context. In send(firstArg, secondArg), the firstArg is presumably the actor you're targeting? I.e a reference to that actor. So you have references to static definitions, and also references to items in context.

An interesting idea is how much specific actor co-ordination/spawning we want to do within Lucy itself - technically actor spawning is all implementation detail! Since it all lives within context/actions etc.

Also 100% agree that Lucy should be independent from XState. XState is a compile target, and Lucy may end up wanting to support different compile targets. That means that we should really be supporting a subset of what is possible in XState, not looking for feature-completeness.

from liblucy.

matthewp avatar matthewp commented on September 24, 2024

Spawned actors are saved as properties on the context (ie, data), so they are data like anything else that gets assigned. When you send a message to an actor you are sending it to that context property, so send(@actorName, eventName) is correct I think. By allowing Lucy to know about the data in a machine we can statically verify that actorName is indeed an actor and not some other type of data.

from liblucy.

mattpocock avatar mattpocock commented on September 24, 2024

One thing that faintly worries me is that if you allow 2+ machines inside a file, eventually folks will ask you for modularity, and import/export syntax. Especially when you could conceivably build very complex actor systems out of this. Not necessarily a danger, but something to bear in mind.

from liblucy.

mattpocock avatar mattpocock commented on September 24, 2024

@matthewp Yes, exactly! Here is the exact type signature you need:

https://www.typescriptlang.org/play?ssl=1&ssc=1&pln=7&pc=2#code/JYWwDg9gTgLgBAbwKIDcCmA7GB5ARgKzQGMYAaOAWQEMiALYDNbMGYCDAZ3KKjSpjTU6DNAF84AMygQQcAOQAPDjH5o5AKHUSArhhJsMcHnwEAVCAHMLAG0E16jADymAwuwEKycU6kzw0npgAJhxwvlh4hCQAfAAUECwGHABccAAKVLDAVNaOQg5Miewczm5YAV4+6FjRcABkiEbuFamuzZ5wotEAlIjqcANwvDDaUIbGqvkipe2V4TBxCP2DK0SzqQmsxQB0a+WeywOi5JtJ3eqiQA

from liblucy.

matthewp avatar matthewp commented on September 24, 2024

Closed by #41

from liblucy.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.