-
Notifications
You must be signed in to change notification settings - Fork 53
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
Control child Microstate creation #357
Comments
Another question. How should we handle deprecations? For example, using the // good
class Human {
name = String;
pet = child(Dog);
}
// good
class Human {
name = String;
pet = relationship(({name}, value) => name.state === 'Bob' ? {Type: Dog, value} : {Type: Cat, value})
}
// bad
class Human {
name = String
pet = create(Dog);
} |
Can you show what you would envision the API looking like if specifying this behavior was separate (both conceptually and on a different line) from the line that defines the child state's existence? |
I think this proposal would benefit greatly from a clear separation between the DSL and Everything Else |
Microstates are already observable. Can this functionality be built on top of that? |
Thoughts on this as an alternative? Do not care about the specific syntax at all. I only care about the declaration of existence and our
|
Right, I think this proposal is about introducing a single primitive, What I'm proposing is to be able to make type Traversal<Parent, Child> = (value: any, parent: Parent) => Child If all For example, we could implement the current DSL as a relationship builder. import relationship from './relationship';
import dsl as legacy from './dsl';
function dsl(...args) {
return relationship(() => legacy.expand(...args));
} And then it would be usable like so: class Counter {
count = dsl(Number);
} But because microstates thinks in terms of relationships, you can use any function in place of |
I don't mean to pick on you, I know you wrote the sample code very fast, but my thought that
|
I feel pretty strongly that this should be synchronous and built in to the core... an abstraction of the relationships that already exist. As for the class Blog {
author = belongsTo(Person);
} and the This proposal is just about the low-level |
I think my stumbling block is that I am treating the requirement to modify Coming from Ruby, I can use "macros" to do things, building on primitives, because they are just functions acting on the class. In Microstates, the |
Can observability/data events be leveraged by having the outer transition method be aware of a list of observers? A transition could do it's thing, then trigger all observers synchronously, potentially modifying the return based on what the observers return, and returning the result of that. (possible nonsense) |
It could but then I think we'd lose some of the special sauce that makes microstates microstates. At the end of the day, the core of microstates is just a directed graph of typed values that know how to transition themselves, and so this proposal is about normalizing (and making extensible) how the edges of that graph are traversed. The observability and events are layered on top in order to react to changes in that graph, but ultimately, the fact that the graph is just one big, single, lazy computation has served us very well thus far, and all the best changes in microstates have lead us toward that configuration, and not away from it. |
The goal here is to make |
This necessity for This is by no means the best option... especially as compilers are now standard toolset for any project. TypeScript really shifts this further. With TypeScript and decorators, we can use One of the advantages of class Human {
@child name: String;
@child pet: Dog;
}
// good
class Human {
@child name: String;
@relationship(({name}, value) => name.state === 'Bob' ? {Type: Dog, value} : {Type: Cat, value})
pet: Dog | Cat
} |
There are many case where we need to specify how a child microstate is created from the parent. A simple case is where you want a microstate to have a default value if none is specified. For example, if I have a counter that I want to start at 1, I would use
create
+ the default value.Another example is one where we want to pass a value down from one microstate to another. For example, if we need to pass the bluetooth service id down to one of its characteristics, then we'd have to use an initializer.
This is ugly, and while there are other ways to achieve this, it's not very clear what's going on. Particularly because we are having to fiddle with the parent value in order to achieve a result in the child microstate.
So we have two one-off use-cases that leverage existing features that were not really created explicitly for those reasons, but we have to use them because they're kinda just laying around.
What if instead we had a general mechanism for determining how microstates are related to each other, and what is better for a general interface than a function? What if we had a function called
relationship
that was used to specify how to traverse a property from a parent microstate to a child microstate? We could use this for both of the use cases above.To set a default value:
the
relationship
function receives the parent instance (counter instanceof Counter
) and the value of the relationship, and is expected to return a microstate representing the relationship.We can use this low-level function to write a "macro" relationship like
defaults
that does what we did above, except more intent-fully.We can also use this low-level mechanism for copying values down the tree:
Again though, we can use a "macro" relationship to define this cleanly:
The implementation of
copyIntoEach
isn't the important thing, merely the fact that we can declaratively define really complex relationships with a little work.Advantages of this approach is that it.
Open questions
The text was updated successfully, but these errors were encountered: