-
Notifications
You must be signed in to change notification settings - Fork 302
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
eval: issues around the function pattern #3711
Comments
@loisch thanks for raising this. Taking a look now. |
Thank's for looking into this @myitcv ! What result should this program have, according to the language specification? |
This turned out to be a bit of a thorny issue. I'll go through my analysis for the benefit of others following along, and will summarise with next steps, links to specific issues etc. All analysis as of 5991735. Given the following repro:
my expectation is that the testscript should pass (note the inverted exit code expectations via
Summarising the result:
Some notes on this repro before my analysis:
My analysis of the above result is as follows:
Specifically regarding point 2. I have outlined my reasoning in I have commented in #3688 (comment) as to why I think our default of Note also how point 2 builds on point 1. If my assertion in point 1 about evalv2 being correct is itself incorrect, much of what follows needs to be revised. Therefore, to expand on why I think point 1 holds, specifically the behaviour of evalv2 with respect to
This passes. We can export the value in the The behaviour demonstrated by
Indeed @loisch has just commented to this effect, in the time since I started composing this reply! I have raised #3715 in order to clarify the spec. That said, what follows assumes that the behaviour of A brief tangent to confirm that both evalv2 and evalv3 agree (modulo exact error counts and messages) on the following:
That is to say, in this smaller example, the Which brings us neatly back to the question of the intent of the user in this issue. In
I have made this observation in #575 (comment). Clearly it's awkward to have authors of such "functions" have to annotate with Returning to Whilst discussing that observation with @rogpeppe, Rog kindly pointed out why the #addn: {
_in: {
val!: int
n!: int
}
_in.val + _in.n
}
x: #addn & {
_in: {
val: 2
n: 5
}
_
}
x: #addn & {
_in: {
val: 1
n: 6
}
_
} which gives:
There is also the fact that with the That said, a package-level visibility modifier combined with downcasts to "drop" the parameters in the result, might offer a solution. All of the above tends to point towards some kind of first-class support for function calls being necessary. SummaryTo briefly summarise the points from above, a sort of TL;DR:
|
Thank you @myitcv so much this detailed analysis and description! It helped me a lot to understand what's going on. My intuition is a bit off because with lazy evaluation in Haskell we also have bottom / undefined but that's the same as non-termination and nothing you can recover from because it's not a regular value. My intuition problem turned out to be the list comprehension. It might help others to show "good ways" and "bad ways" to solve problems. The function in this example uses a list comprehension for a case distinction on the type of the input. That's not a good idea. At least it might not do what you intend. For example I expected that an if-clause with a check like
It's much better, idiomatic, safer and simpler to use a disjunction to define the "case distinction" function:
The list comprehension also needs the "impossible" case. I don't really understand why I get
when leaving it out... Which value of Still so much to learn! |
Absolutely, and that's what we're looking to do with our worked examples on https://cuelang.org/.
I'm not convinced it's totally wrong. Per my analysis above, the problem is not that CUE is failing to detect the error in the In the meantime your switch statement needs to cover the three possible states of the
Hence the need for the Another alternative would be to write:
in lieu of the |
Thank's for explaining the "main" issue. That's why I propose using the disjunction because the alternative is only valid if the whole struct is valid. I can't get a value for This example from my comment above is different
I don't see where It again feels like a bug. I think the problem is that I gave the example because many beginners including me would think that it behaves like this Python program
It seems the function maps |
Apologies, for some reason I entirely skipped over reading that part of your reply! My immediate thought is where that pattern applies it probably offers quite an elegant solution for now. Albeit better if we had more first-class function support, so the errors can be more precise.
A general point to start with: I think the other approach we are discussing, with the list-based switch, is going to fall short in a number of ways, not least because we don't have all the necessary builtins to be precise. See #943 for an explanation of various options that ultimately allow the comparison with Moving on to the various behaviours we are seeing. (And apologies if anything that follows misses the point you are making - I'm trying to keep up with various threads in various issues!) This repro passes:
This is expected to my mind, because
We don't have the builtins to express a constraint on There is an alternative:
Note that this still leaves us in the situation where
So we really need the builtins from #943 in addition to |
Oh, thank you so much, @myitcv ! It's like private lessons and I very much appreciate it! It's clear now. :-) CUE is fascinating! I read #943 and I think these changes (function-call and definition syntax, isconcrete, etc) would be really helpful! They won't make CUE simpler (but also not more complex in it's core) but they will make it a lot easier to get started and "do the right thing". Syntactic sugar introduces the patterns you should use and allows us to map concepts known from other languages into the space of the new language without realising that the underlying execution model is very different. Without the "do" syntax which allows Haskell code to look like imperative language code, Haskell would have died without ever being seen, because this allowed pragmatic programmers to get things done without having to first understand lambdas and monads and the bind operator ( Keep up the good work! I think CUE is well worth the learning curve! And about getting things done: My configuration generator proof-of-concept is working now flawlessly which v2 and v3 and I'm very happy! |
Thanks so much for taking the time to engage so comprehensively here, @loisch. This issue helped me (at least!) to some very interesting realisations regarding the choices we have for more first-class "function" support. And also helped to reinforce the ideas in #943 and #575, albeit through a different lens. Once we have the critical bits for evalv3 off the, well, critical-path, we can hopefully return to these points in relatively short order. |
still reading through all the issues mentioned. probably not the greatest take as i was writing in a hand-wavy nature. this was the cue i made to try to generically handle this issue, but having to call it every function is not performant at all due to lack of structural sharing. it's still pretty performant for one shot use cases, but using it for every function dispatch i was getting 40 second eval times for like 50 lines of json.
|
this was my function path flow introspector. categorized, enumerates, and canonicalizes input states and functions that are available as a result of those states and presents results that have the coordinates to their actual objects in the input/output data stored in-band. looping on and matching with the output structure of this builtin should allow the cuelang developer to make the decision about when to render
at each depth, ((bag, item), shape) is assessed. if item and shape are both lists/maps and max depth hasn't been reached, dive deeper according to a a couple things may be off in the schema below
|
maybe
to me after using cue for a year and just programming in-general if i were to put human words on syntax operators these would be them.
|
What version of CUE are you using (
cue version
)?0.12-rc1
Does this issue reproduce with the latest stable release?
Yes and no. Old and new evaluator both produce the same result with version 0.11.1 but still the test doesn't fail (as it should, in my opinion). The new evaluator in version 0.11.1 produces the same result as the old evaluator.
What did you do?
What did you expect to see?
A failing test.
cue vet
should not accept this program because#fun1.in
must unify with#TypeA
butab
does not unify with#TypeA
. Also#fun1.in
is used it the calculation of the result (call.out
), so it influences the result and it should have_|_
as value and thuscall.out
should be_|_
, too. If my intuition is wrong and this program is indeed correct it should still have the same result in both evaluators.What did you see instead?
A passing test showing that old and new evaluator produce different results and that a validity constraint (
in: #TypeA
) is being ignored.The text was updated successfully, but these errors were encountered: