-
Notifications
You must be signed in to change notification settings - Fork 9
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
Internal vs external validation #26
Comments
Here's an idea that's been bouncing around in my head: One of the concerns with internal validation is that, by creating an extra variable that holds the value of the view, we are duplicating data unnecessarily—we have to create a copy of what's in the view to put in the binding variable. In the report I suggested one possible solution to the duplication: that the binding variable would not actually hold a value itself, but would be a special kind of proxy-variable, using the view to hold the value and the read/write operations provided by the binder to access the value. So the new idea is that the binding variable does hold a value, but that the value it holds is actually the view itself. Then the read and write operations would be used (along with any conversion/validation functions) to create a constraint between the binding variable and target variable. So in one direction we would have a method which takes the view as input and (using the read operation) produces a value for the target variable as output. In the other direction is a method which takes the target variable and “produces” a view—but, of course, it would be the same view, just (using the write operation) updated with the new value. The event handlers for widget-change events would simply touch the binding variable. I'm not sure if this is actually any better than the proxy-variable approach. Both avoid the unnecessary duplication problem. I guess the advantages of this approach are:
I suppose the advantages of the proxy-variable approach is:
Of course, both of these approaches create a scenario where a model containing binding variables cannot be evaluated until binding is completed, which can be viewed as a disadvantage. And I'm not sure we really have a use-scenario yet which justifies either of these approaches. P.S. Sorry—didn't mean to close the issue; just clicked the wrong button. |
One quick note before my laptop dies: I keep seeing talk of "temporary object" to overwrite a method output. One thing I plan to support is direct manipulation of outputs in methods instead of returning replacements. This goes with the desire for rich mutations beyond assignment. |
With direct manipulation of outputs in methods, do you mean that a method is a procedure that reads the old value of its output(s), and can modify it (e.g. calling a method instead of assigning to it?) E.g.: Assume two vars A, B and a method m from A to B. The "old" model is that m: A -> B, the "new" model will be ? |
Going through the report: Validation FunctionI don't think it's necessary to support "an arbitrary sequence of either-value-or-error/maybe-error functions". The programmer could perform the composition manually (and will have to anyway for more complicated combinations of converters/validators). Error ReportingI am not a fan of the "implicit error as a property of the target variable" default behavior. In other cases where we provide defaults, it is generally the most commonly seen explicit specification. In this case, however, it would never be seen as an explicit specification since programmers generally should not set properties on proxies (for fear of overwriting something needed by HotDrink). I think we should require explicit specification for every case that needs to bind to an error. Effects on Space ComplexityI like the approach in Gabe's comment above where the variable's value is actually the view. It mimics closely our existing approach. However, the trouble with evaluation-before-binding is a problem that needs to be solved before we can implement internal validation. Perhaps wrap the binding variable creation in a constructor that gets passed to hd.bind (building off what is proposed in the Modular Design section)? Partial-Value BindingGiven the example, I think this should be called "Property Binding". If the view wants to manipulate a single property without changing the whole object, how can observers discover that the object has changed? Either the property needs to be an hd.variable, or we need a new proxy, e.g., In-Model ValidationI don't think this should be a goal for internal validation. A different binding variable would have to be created for every method that outputs the variable, and it could create a cycle in the case of self-loops. Besides, validating view bindings is enough work. :) |
Direct mutation (I should not have said "manipulation") of outputs is just a syntactic change that enables some optimization by better reflecting the semantic distinctions among mutations. Taking your example of a method .method(B, function () {
var b = B();
b.foo = A();
return b;
}); However, since this method returns an "identical" value for its output1, the evaluator will assume it is unchanged, and the method's mutation will go unobserved by every other method and subscriber. To make the mutation observable, we must choose an unintuitive, inefficient method implementation that constructs and returns a copy of .method(B, function () {
var copy = {};
Object.extend(copy, B);
copy.foo = A();
return copy;
}) Direct mutation is intended to change this. Assuming .method(B, function () {
B.prop("foo", A());
}); Footnotes
|
Thanks for the feedback! Here's my thoughts
I agree it's not necessary; here's a couple of ideas as to why it might be helpful.
Yeah, that's a good point. I still think, though, that having to create/specify every error variable manually is going to get old really quickly. Maybe we need to spend some time thinking about namespaces. If we're going to support modular composition of models then we're going to need some way (even if it's just a convention) of organizing and referring to those different variables. Maybe if we had a scheme for that then maybe we could apply it to creating good names for automatically-created error variables.
Here's a thought: A binding constraint (created when you create a binding variable) would consist of special methods which have properties: a read (or write) function and possibly some validation functions. The body of the method would take the value of the binding variable, use its read function on it to get a value, use the validate function to test the value, then return that. So by default, the read function could just be the identity function, meaning the value of the target variable is stored directly in the binding variable. Later on when binding occurs we can replace the value of the binding variable with the view, and replace the read function with the actual read operation provided by the binder. We can also use the previous value of the binding variable to initialize the view (via the write operation).
This seems very closely related to the other comment thread going on in this issue related to direct mutation of output variables. So it might be worth noting here that any solution we come up with for methods being able to perform mutations on the output will also work with internal binding, since internal binding is just a method.
My motivation for this comes from my comment in #24, where we want to validate number of nights, but we still want to use a constraint to calculate number of nights. I think the best way to support that is with in-model validation. The other alternatives are 1) don't use the constraint, 2) have the validator duplicate the work of the constraint, 3) use a pre-condition instead of a validator, in which case the bad value gets through. |
Okay, those are good motivations.
I think we should wait for common uses to arise before attempting to provide good conveniences. An obviously good design may emerge once we have something to look at.
Do we have this already by packaging models into constructors?
Correct.
I don't think I completely understand this. It might be a discussion best done in person with a whiteboard.
I understand, and I still don't like the approach. I want to avoid creating new variables that get injected into the graph the programmer built (read: change their method's inputs or outputs). Right now, we are only discussing new variables that wrap/extend/sit outside of the graph the programmer built. I left a comment in #24 as well, giving my preferred solution (with reasons): guard the method. Execute the method only when the guards pass. With the direct mutation of outputs feature, when the method fails to execute, the evaluator will know the outputs were unchanged and won't execute the downstream methods or notify subscribers. |
Just to clarify Gabe's idea, which I think is very clever:
A binding variable is created at the same time as the target variable. Since this occurs before binding, it starts with a non-view value (presumably the current value of the target variable). Also created is a binding constraint of two methods connecting the binding variable to the target variable. In the direction of binding variable to target variable, the method has a few properties:
These properties are used by the method each time it executes. If the system evaluates before binding occurs, then the The method for the opposite direction, from target variable to binding variable, is similar, e.g., with Errors returned by either method are stored on the binding variable to be read by When binding occurs, we use the value of the binding variable to initialize the view, then we replace the binding variable's value with the view. That means the |
Gabe has written an excellent report comparing the two main approaches to validation: with special options for binders (external), and with binding variables that extend a view-model (internal). I uploaded the report to the project website so that we could discuss it here.
Issue #24 relates to this discussion in part, but I wanted to start a new thread so that that issue can stay focused on multi-view validation specifically.
The text was updated successfully, but these errors were encountered: