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

Idea: Better way to get two-way property references #1012

Open
LeaVerou opened this issue Jan 11, 2024 · 0 comments
Open

Idea: Better way to get two-way property references #1012

LeaVerou opened this issue Jan 11, 2024 · 0 comments

Comments

@LeaVerou
Copy link
Member

Right now, all values passed around in MavoScript have to be objects. Even simple strings or numbers. These objects do go to great lengths to behave like their primitive counterparts, but when handling them in raw JS, there are many cases where they behave differently.

Not only does it mean it may not play well with third-party utility libraries, it also makes it very hard to extend Mavo with custom functions.

The reason it was done this way was so that values could contain a link back to the property that produced them (via value[Mavo.toNode]), which was essential to implement Actions.

I’ve thought of a better way.

So, the primary issue is primitives, right? When we have objects, we have ways to get the Mavo node (either through Mavo.toNode or a WeakMap that matches data to nodes. This means that for a primitive, if we have the property that gets us that primitive from its parent, then we can track down the node that produced it!

But how do we pass that to the function? We already have a needsContext parameter that some functions use (phrase() and pluralize() rn) because they need to access the DOM. What it does is it passes $this as the function context.

We could extend this to pass in an object of metadata, of which $this would just be a property. Another property of that object could be parent and property references for every function argument that is not the result of an expression. Something like:

{
	$this: (...)
	args: [
		{parent: (...), property: "foo"},
		null /* expression */
	]
}

I think for the vast majority of action cases, the reference to the data that needs to be manipulated is a single argument (e.g. set(counter, counter + 1)).

There are some problematic cases where functions are used to subset or transform the data source to be written, e.g. things like set(first(done), true) or set(done where done=false, true) . Since first(done) or done where done=false is an expression, this solution would not help. However, we could define first() or where as needing this kind of metadata. Furthermore, this object could contain a property for result references that the function would populate:

{
	$this: (...)
	args: [
		{parent: (...), property: "foo"},
		null /* expression */
	],
	result: { parent: (...), property: 0 }
}

or for something like where:

{
	$this: (...)
	args: [
		{parent: (...), property: "foo"},
		null /* expression */
	],
	result: [
		{ parent: (...), property: 0 },
		null,
		{ parent: (...), property: 4 }
	]
}

For performance, this would only be done when needed, and would be passed down to descendant function calls. Therefore, in the beginning, only actions would do this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant