Skip to content
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

Abstract relationships. #358

Merged
merged 5 commits into from
Oct 8, 2019
Merged

Abstract relationships. #358

merged 5 commits into from
Oct 8, 2019

Conversation

cowboyd
Copy link
Member

@cowboyd cowboyd commented Apr 25, 2019

See #357 for full details.

How to contextualize a child microstate has been a constant challenge. In other words, sometimes you want a child to take on a constant value. Sometimes you want it to take on a default value, and sometime you might want it to be completly polymorphic on what value it takes on depending on the parent.

Unfortunately, this is not possible with our current way of expressing relationships with is either with a DSL:

class Counter {
  count = Number;
}

or with an explicitly created microstate:

class Counter {
  count = create(Number, 1);
}

The problem is that there is no way to expand the concept of what gets created if you want to do something fancier, like copy values down the graph without resorting to intializer hacks.

This introduces a very thin concept of a relationship between a parent microstate and a child microstate. The responsibility of the traversal is to take the value at the location of the target microstate and return a specifier for the child microstate.

To do this, we introduce the concept of a "Cell" which is a Type, value pair { Type, value }. This is the minimal about of information required to specify a microstate. This is super light-weight because it does not require the overhead of actually create-ing a microstate.

It is the responsibility of a relationship to resolve a value into a cell.

Note: in future microstates, we will use { Type, path } as a cell
specifier.

The relationship class is organized monadically so that it can be mapped, flatMapped, etc... and any relationship can be extended using map and flatMap

For example, to implement a hypothetical child helper which creates a specifies a substate of a given type and default value, it could look like this.

function child(Type, defaultValue) {
  return relationship()
    .map(({ value }) => value != null ? {Type, value} : {Type, value: defaultValue })
}

This pull request does not introduce any actual changes built on the relationship function, but just exposes it so that we can experiment with it.

@cowboyd cowboyd force-pushed the cl/abstract-relationships branch from 9a74458 to e335caf Compare April 25, 2019 19:55
@coveralls
Copy link

coveralls commented Apr 25, 2019

Coverage Status

Coverage decreased (-0.1%) to 98.74% when pulling aabfdf3 on cl/abstract-relationships into bd8529c on master.

@cowboyd
Copy link
Member Author

cowboyd commented Apr 25, 2019

One thing I was thinking was that instead of using (value, parent) => Cell as the arguments, we could use an object so that we can make it rich with metadata.

class Edge {
  get targetValue() {
    return view(At(this.name), this.parentValue);
  }

  get parentValue() {
    return valueOf(this.parent);
  }
   constructor(parent, key) {
     this.parent = parent;
     this.name = name;
   }
}

And then the relationship traversal would receive an Edge object:

type Traversal = (edge: Edge) => Cell

@cowboyd cowboyd force-pushed the cl/abstract-relationships branch 2 times, most recently from 1d3908f to 74a5def Compare April 25, 2019 21:19
@taras taras self-requested a review April 25, 2019 21:50
Copy link
Member

@taras taras left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should release this as a beta and test out in all of the use cases that we have.

@cowboyd
Copy link
Member Author

cowboyd commented Apr 25, 2019

Here is a branch using an edge argument to the traversal function, rather than passing (value, parent) separately. This lets you have ready access to handy things like the location's value as well as its parentValue https://github.com/microstates/microstates.js/blob/cl/abstract-relationships-using-edges/src/relationship.js#L40-L58

@taras
Copy link
Member

taras commented Apr 25, 2019

Edge looks like the way to go because it gives us a richer object to use in the future. Should I create a beta release from edge?

@cowboyd cowboyd force-pushed the cl/abstract-relationships branch from 74a5def to f1c1d37 Compare April 26, 2019 16:01
@cowboyd
Copy link
Member Author

cowboyd commented Apr 26, 2019

@taras This PR now uses the Edge object as the object to resolve relationships, and also exports the relationship() function, and should be good to go w.r.t beta.

@taras taras mentioned this pull request Apr 26, 2019
@taras
Copy link
Member

taras commented Apr 30, 2019

Here is a TypeScript definition for relationships https://gist.github.com/cowboyd/5fd38eb194fc362457909d9dd762f15e

@cowboyd cowboyd force-pushed the cl/abstract-relationships branch from 7fd81fa to 0d8c44e Compare October 4, 2019 16:36
How to contextualize a child microstate has been a constant
challenge. In other words, sometimes you want a child to take on a
constant value. Sometimes you want it to take on a default value, and
sometime you might want it to be completly polymorphic on what value
it takes on depending on the parent.

Unfortunately, this is not possible with our current way of expressing
relationships with is either with a DSL:

```js
class Counter {
  count = Number;
}
```

or with an explicitly created microstate:

```js
class Counter {
  count = create(Number, 1);
}
```

The problem is that there is no way to expand the concept of what gets
created if you want to do something fancier, like copy values down the
graph without resorting to intializer hacks.

This introduces a very thin concept of a _relationship_ between a parent
microstate and a child microstate. The responsibility of the traversal
is to take the value at the location of the target microstate and
return a specifier for the child microstate.

To do this, we introduce the concept of a "Cell" which is a Type,
value pair `{ Type, value }`. This is the minimal about of information
required to specify a microstate. This is super light-weight because
it does not require the overhead of actually `create`-ing a
microstate.

It is the responsibility of a relationship to resolve a value into a
cell.

> Note: in future microstates, we will use `{ Type, path }` as a cell
  specifier.

The relationship class is organized monadically so that it can be
mapped, flatMapped, etc... and any relationship can be extended using
`map` and `flatMap`

For example, to implement a hypothetical `child` helper which creates
a specifies a substate of a given type and default value, it could
look like this.

```js
function child(Type, defaultValue) {
  return relationship()
    .map(({ value }) => value != null ? {Type, value} : {Type, value: defaultValue })
}
```
@cowboyd cowboyd force-pushed the cl/abstract-relationships branch from 0d8c44e to aabfdf3 Compare October 8, 2019 18:12
@github-actions
Copy link

github-actions bot commented Oct 8, 2019

This PR is available to use:

npm install [email protected]

You can view the NPM package here.

Generated by 🚫 dangerJS against aabfdf3

@cowboyd cowboyd merged commit 2ce0c29 into master Oct 8, 2019
@cowboyd cowboyd deleted the cl/abstract-relationships branch October 8, 2019 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants