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

document cel.Constant environment option #920

Open
brigitops opened this issue Apr 8, 2024 · 1 comment
Open

document cel.Constant environment option #920

brigitops opened this issue Apr 8, 2024 · 1 comment
Assignees

Comments

@brigitops
Copy link

brigitops commented Apr 8, 2024

I'm finding it hard to figure out how to use the cel.Constant environment option. As it is an environment option, I assume it is intended for general use and not as an internal feature?

For cel.Variable, examples abound, such as this from the main README:

import "github.com/google/cel-go/cel"

env, err := cel.NewEnv(
    cel.Variable("name", cel.StringType),
    cel.Variable("group", cel.StringType),
)

The same is not true of cel.Constant. A thorough search of the internet, for various combinations such as "cel-go", "common expression language", "Constant", "cel.Constant", and a bunch of others I tried, all find very little to explain this feature.

I tried to guess at it, but ref.Val has me unsure:

import "github.com/google/cel-go/cel"

env, err := cel.NewEnv(
    cel.Constant("name", cel.StringType, "how to create a valid ref.Val?"), // this won't compile, as ref.Val isn't a string type
)

What is the safe, intended way in which I should create a ref.Val for a primitive type such as a string or int?

Also, are constants automatically optimized by evaluating them at compile-time?

I've raised this as an issue rather than discussion, since I think it will help other uses if the answer becomes documentation.

@TristonianJones TristonianJones self-assigned this Apr 22, 2024
@hyp0th3rmi4
Copy link

I have used constants in my cel implemenation, and my understanding is that the underlying implementation of cel.Constant eventually leads to the declaration of a Variable with an assigned value:

func NewConstant(name string, t *types.Type, v ref.Val) *VariableDecl {
return &VariableDecl{name: name, varType: t, value: v}
}

as a result is then appended to the list of variables in the environment.

When the AST is created and checked via the Env.Check the checker is going to set the the value of identifiers in the reference map stored in the AST:

https://github.com/google/cel-go/blob/master/checker/checker.go#L109C25-L109C42

This causes a cel.Constant(...) to be assigned during AST check.

Once the value in the reference map is checked, the planner is going to traverse the AST and replace the reference to the constant with a constant value:

https://github.com/google/cel-go/blob/master/interpreter/planner.go#L113-L148

The NewConstantValue(....) method produces an InterpretableConst, whose eval method returns the pre-configured constant set at the time of specifying cel.Constant(....) disregarding whatever mapping is passed via the Activaiton argument (see below)

https://github.com/google/cel-go/blob/master/interpreter/interpretable.go#L178-L203

Because the planner.Plan method is executed as part of the creation of the interpretable, which is setup as part of the method: newProgram, which creates the program for the evaluation.

https://github.com/google/cel-go/blob/master/cel/program.go#L182-L294

This ensures that a constant assignment is respected and it is not possible to override the value originally set via variable (AFAIK).

To answer your questions:

  • constants identifiers in the expressions are replaced with their values during program creation (the process is a bit more complex than that, but the effect is the same)
  • because constants are initialised with a ref.Val, you can use the primitives for primitive types that are defined to create the corresponding cel representations (see: "github.com/google/cel-go/common/types"):
    • types.String(string)
    • types.Bool(bool)
    • types.Double(double)
    • .....

These types are aliases of the go types at the moment, so the standard type constructor will do.

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

No branches or pull requests

3 participants