-
Notifications
You must be signed in to change notification settings - Fork 81
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
Accessing late-bind field fails #1915
Comments
The issue is the following: In the definition of
Which is fair. However, line 37, you do:
This is projecting the Merging it with subsequent data won't cause the field to recompute, because Nickel considers that Maybe it looks like it could work here because Now, I can see two ways of fixing your issue and get the result you want: the first one is simply to perform the merge before extracting the value, so that
Here, we are overriding Another solution is to avoid relying on What you can do is to just declare some fields without definition, which work a bit like function parameters: it's just a way to say "I expect those fields to be there at some point thanks to merging" and to satisfy Nickel's scoping rules. You can then use
(you could in fact even declare an undefined field |
Thank you @yannham . The clarity of your answer is enjoyable as always.
Somehow I have the wrong mental model that the contract A follow up question: Is there some improvements that I could make to simplify the wiring of passing
The current setup reminds me of writing React web app in the early days where passing props down the component tree layer by layer is the only way for a child node to use a value higher up in the tree hierarchy. |
It's true to some extent that we could add some rules that says that contract application might bring new variable into scope. Though OOP and contracts are still quite different. On issue in Nickel is that we don't have different kinds of "record". If you think about it, in general purpose languages, there are different instances of "a bunch of things bundled together with name": classes definitions, interfaces, records, record types, modules, etc. They have many variations, but usually what's important in e.g. a module interface is that you can statically (and obviously) deduce which symbols are present. Thus, you can have things like In Nickel records are potentially dynamic, so if I write We can come up with reasonable rules, which are informally extracting obvious static information and would stop as soon as something can't be obviously determined statically. So the previous The issue with those smart guesses is that it can be very surprising for users once it suddenly stops working. Everything seems to work as it should, magically, and then one change to a record contract hits the limits of the rule, and you get unbound identifiers all around. I think that's why we have been reluctant to apply those kind of rules until now. Another reason is that, if we apply them, they should be properly formalized - this might actually come to fruition for the Another solution is to do like other languages and have restricted form of records that can't be too dynamic: modules, contracts, etc. However, the cost is complexity - especially for the occasional editor of a Nickel config who then have to understand the differences between all of those.
I understand it's a MRE, but it's a bit hard to know what you're trying to achieve on this specific example. I think one of our assumption with those lazy inputs is that you wouldn't need each and every layer to have its own separate inputs, and they could all share the one coming from the top-level record. So same question here: do you really need each subrecord to have its own separate |
Totally agree that any half-baked, informal, "smartish" rules makes for worse UX, because when they fail, we as human have a tendency to think of it as "trying to be smart" but really "is just dumb", while not recognizing it being smart when it does the "smart" things correctly. Though I'm not following on the relation between the topic of this issue i.e. "a contract bringing variable into scope" and the
I'm experimenting with modularization. By making each level a "module" with its own inputs (as the new "Modular Configuration" doc section suggests), they become "self-contained" thus composable with other modules. My experiment defines contracts for each module, then wires the inputs together when composing smaller modules into larger ones. When that is done, the final configuration can be splitted (by domain logic) into smaller files (also modules) applied with their respective contracts. After I corrected my codebase based on your suggestions, I realized the problem with my previous MRE: the |
Yes, the connection isn't all too obvious. The conceptual issue we encounter with But the typechecker now needs to statically resolve a record access before evaluation. So you end up with the same dilemma as for deciding what bindings are brought into scope by a contract: either we need a restricted notion of a record (say, a module), which guarantees that you can easily resolve such accesses statically, or "you try to be smart" when all of Good to know that your experiment turns out well! |
Ah I see, so the similarity is the dilemma of being formal but restrictive or "being smart". Thanks again @yannham for all the guidance and insights. |
Describe the bug
I apologize for the over-generic & cryptic issue title, as it is really hard to summarize the issue in words, so here's the MRE.
To Reproduce
The program looks like this:
Evaluating the program, providing inputs to the
prof1
field, we get the following error:Commenting out line 26,
prof1_
evaluates as intended:Expected behavior
prof1_
evaluates to:It feels like
zab
should be able to access the definedinputs.a
.Environment
Additional context
Add any other context about the problem here.
The text was updated successfully, but these errors were encountered: