-
Notifications
You must be signed in to change notification settings - Fork 298
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
Proposal: core builtin extensions #943
Comments
Original reply by @seh in cuelang/cue#943 (comment) This is so good to see. One problem to consider with the "Switch" section: You write, more or less, |
Original reply by @seh in cuelang/cue#943 (comment) Also, while |
Original reply by @mpvl in cuelang/cue#943 (comment) @seh: yes, Regarding I'm not sure I understand the point with the null values, but maybe this answers your question. Do you think adding |
Original reply by @seh in cuelang/cue#943 (comment) I was not sure that CUE has the same notion of "null" values that SQL, HCL, Jsonnet, and other languages have, so the semantics of a hypothetical I don't think Would it be possible to write a CUE "function" that encapsulates your |
Original reply by @mpvl in cuelang/cue#943 (comment) @seh: you can do else with the switch approach and I’m not in favor of a dedicated If-else construct, as it encourages bad patterns. But I see your points otherwise. I guess you could indeed express this as cue macros neatly if we had the call shorthand.
One problem is that the first element cannot have a conflicting definition of #0. But maybe this is enough for now to just point out the pattern and suggest that people comment the construct:
This would not require any additions to the language and we can get some experience to see what works. The query addition may also provide useful patterns that obviates the need for this. |
Original reply by @mpvl in cuelang/cue#943 (comment) @seh in CUE, bottom (incomplete errors, |
Noting that one use case of comparison against |
Perhaps this warrants a new discussion or feature issue, but one thing I've found lacking in cases where I've wanted something like the list-as-choice pattern at the end there comes from FP paradigms doing pattern matching. Specifically, language support for guaranteeing that the options are exhaustive. Reflecting here that the list comprehension version of this provides that in a roundabout (and at-runtime) way: if all the alternatives fail, the list is empty and the index will be out of bounds. So there's some safety railing there. But: the user is going to get an "index out of bounds" error, (which is confusing when the cause is that an alternative was overlooked), and it'll be the user of the CUE program and not its author who gets the error. It would be fantastic to have a language level |
The default seems to prevent the out of bounds issue, assuming it is always required. One could use It may be useful to know that something like It might be also worth considering that, in many ways, CUE comes from Go and there is value in minimizing language features and syntax. |
What about an operator for like I'm trying something like
which won't work, I think something like this might
The goal of the example is to turn CUE types into a string, maybe there could be a builtin or stdlib package that helps with that in a more targeted way. A |
Strong +1 to that - a native subsumption operator is a key roadmap item for thema (née scuemata). For now, the necessary enforcement of a subsumption relation has to be done in Go. (Though that doesn't work either because of a panic that i need to post an issue for, once i have a clear reproduction) |
What's the general status on the extensions discussed here? I'm particularly interested in functions. |
Noting that we should also consider downcasts #454 in scope of new builtins. |
Noting what I think is a tricky edge case here:
The definition |
I came here from https://cuetorials.com/patterns/functions and am especially interested in the functions syntax, but I have yet to have a use case for any of the other proposals. Should some of these use cases be split out into different issues? I feel like there's a lot being proposed here. It might make it clearer which features are priorities to users if these were separate issues. |
I have just created #3165 for further discussion regarding the encoding of oneofs in CUE. |
This prepares for both adding new buitlins (such as the proposed numExist et. al.) as well as adjusting some exiting ones, like `and`. This CL is supposed to be a no-op (aside from adding the functionality) and we separate it out to make future diffs smaller. We will test RawFunc itself with the respective builtins. The issue with `and`, for instance, is that it "weaves" in partially evaluated expressions into existing evaluation. In come cases this may lead to cycles. To prevent this, there needs to be a back channel from the function to the evaluator. Only the function can know exactly which cycle information is needed. Other uses are functions like `numExists` or any other builtin that needs to operate on CUE expressions rather than values. Issue #943 Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com> Change-Id: I32ef92bfdc2a8318b00801bc067df4a073a10a73 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1202442 Reviewed-by: Matthew Sackman <matthew@cue.works> TryBot-Result: CUEcueckoo <cueckoo@cuelang.org> Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
This prepares for both adding new buitlins (such as the proposed numExist et. al.) as well as adjusting some exiting ones, like `and`. This CL is supposed to be a no-op (aside from adding the functionality) and we separate it out to make future diffs smaller. We will test RawFunc itself with the respective builtins. The issue with `and`, for instance, is that it "weaves" in partially evaluated expressions into existing evaluation. In come cases this may lead to cycles. To prevent this, there needs to be a back channel from the function to the evaluator. Only the function can know exactly which cycle information is needed. Other uses are functions like `numExists` or any other builtin that needs to operate on CUE expressions rather than values. Issue cue-lang#943 Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com> Change-Id: I32ef92bfdc2a8318b00801bc067df4a073a10a73 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1202442 Reviewed-by: Matthew Sackman <matthew@cue.works> TryBot-Result: CUEcueckoo <cueckoo@cuelang.org> Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Originally opened by @mpvl in cuelang/cue#943
Definitions
Before we introduce some of the proposed builtins, we formally introduce some as-of-yet undocumented language features.
Functions
We propose cue supports named argument functions and calls to “structs” as a shorthand for the common macro pattern (e.g.
(s & { _, a: x}).out
).A function argument is now defined as:
Any named argument must be followed by other named arguments.
The expression
s(a: x, b: y)
, wheres
is a struct, is now a shorthand fors & {_, a: x, b: y)
.Validator
A validator is a special builtin that is evaluated by unifying it with other values whereby the result is one of a few outcomes:
_
if the validation is successful and making the value with which it was unified more specific does not change this result (or it is a final evaluation).A validator must be run at the last stage of evaluating a node, after a fixed point is reached evaluating all all non-validator values, in which case any error is considered a fatal error. A validator may be run at earlier stages of the evaluation of a node, in which case an incomplete error signifies that the decision on validity must be postponed.
An example of a language-level validator is
<10
.struct.MinFields
andstruct.MaxFields
are examples of validators of builtin packages.Validators can be thought of as a Go function that has an error return signature.
Inferred validators
Optional: Builtin functions that have the signature
foo(x1, x2, …, xn) bool
may be implicitly interpreted as validators of the signaturefoo(x2, …, xn) error
.The CUE function notation
We define the following signature format for cue functions:
Either all or none of the arguments should be named.
The following rules apply for calling functions with this signature:
These rules could be relaxed later.
Proposed builtins
builtins to replace
_|_
(bottom)Although
_|_
is part of the standard CUE idiom, it has several issues:We intend to deprecate the bottom symbol (keeping it around for backwards compatibility) and replace it with builtins that clearer conveys the intent of its usage.
Comparison is not supported by the spec (arguably), but it is a crucial piece of functionality for many CUE configurations. The meaning of it is unclear, however. In many cases, it is used to check whether a reference exists. In some cases, however, the intended meaning is to check that a value is valid. In reality, CUE implements a semantic that is somewhere in between the two cases: it checks the validity of a value, but not recursively.
Note that if any of these builtins return false, they may still be satisfied at a later point in time. Evaluation should take this into account, as usual.
_|_
replacement:error(msg: string | *null) :: _|_
The use of
error(msg)
replaces the common use of_|_
with the added ability to associate a user message with an error. When used within a disjunction, the error will get eliminated as usual, but upon failure of the disjunction, the user-supplied error is used as an alternative error message.Comparison to bottom
Uses of comparison against bottom will need to be replaced with one of the following builtins.
isconcrete(expr) :: bool
isconcrete
reports whetherexpr
resolves to a concrete value, returning true if it does and false otherwise. It is a fatal error if an expression can never evaluate to true.Example:
Purpose: replaces
if a.foo != _|_ {
, where it is checked whethera.foo
exists with the purpose of determining whether it is a concrete value.exists(expr) :: bool
(optional)exists
reports whetherexpr
resolves to any value.Example:
Purpose: replaces
if a.foo != _|_ {
, where it is checked whethera.foo
exists regardless of concreteness.validator builtins
must(expr: _, msg: string | *null) :: _
must(expr)
passes ifexpr
evaluates totrue
and fails otherwise.Must can be used to turn arbitrary expressions into constraints. For instance,
a: <10
can be written asa: must(a < 10)
. See Issue #575 for detailsnot(expr) :: _
not(expr)
passes if unified with a valuex
for whichexpr&x
fails and false otherwise.See #571 for details.
Examples:
numexist(count, ...expr) :: _
numexist(count, ...expr)
passes if the number of expressions for whichexists(x)
evaluates totrue
unifies withcount
.The main purpose of
numexist
is to indicate mutual exclusivity of fields.numconcrete(count, ...expr) :: _
(optional)numconcrete(count, ...expr)
passes if the number of expressions for whichisconcrete(x)
evaluates totrue
unifies withcount
.numvalid(count, ...expr) :: _
(optional)numvalid(count, ...expr)
passes if the number of expressions for whichisvalid(x)
evaluates totrue
unifies withcount
.Builtins related to concrete values
Purpose: combine schema of different instances of the same package that would otherwise fail because there are conflicting definitions.
manifest(x) :: _
manifest
evaluatesx
stripping it of any optional fields and definitions and disambiguating disjunctions after their removal.Use cases:
Defining ranges
Looking around at other languages, defining range numbers clearly is a hard problem, as it is often not clear from just looking at the syntax, or even wording, whether or not ranges are inclusive.
CUE’s unary comparators provide a possible solution to this issue.
range(from: int, to: int, by: int | *1) :: [...int]
Builtin
range
returns a stream of values, starting fromfrom
(must be concrete) , addingby
(defaults to1
) as long as unification withto
succeeds. It is an error to define a range that never terminates.Examples:
Switching
CUE’s
if
is not paired with anelse
. This is partly becauseif
really is a comprehension. But another reason is that the use ofelse
quickly leads to nested conditions. Aswitch
statement is generally more conducive to readability in this case.A switch statement can be simulated in CUE using lists:
is equivalent to the hypothetical
The issue is that the hidden
[0]
at the end of theswitch
is impairing readability.head
A
head
builtin could make the above more readable. It would do nothing more than select the first element in a list, but doing so by more clearly signaling the intention at the start of the list.Package
std
We’re considering making all core builtins available under the package
std
, so that they can be referenced unambiguously and more clearly than using the__
prefix.The text was updated successfully, but these errors were encountered: