Evaluation is core CUE and powers everything from unification to validation.
This page will show you how to run the evaluator from Go and
configure the options to control its behavior.
Value.Validate()
Validate runs the evaluator on a value and returns an error.
We’ve seen the function used without arguments.
Validate will also use the many cue.Options so you can control the process.
These are the same options used in Value.Syntax(...Option).
validate.go
packagemainimport("fmt""cuelang.org/go/cue""cuelang.org/go/cue/cuecontext""cuelang.org/go/cue/errors")constval=`
i: int
s: string
t: [string]: string
_h: int
_h: "hidden"
#d: int
#d: "bar"
`funcmain(){c:=cuecontext.New()v:=c.CompileString(val)// try out different validation schemesprintErr("loose error",loose(v))printErr("every error",every(v))printErr("strict error",strict(v))fmt.Printf("\nvalue:\n%#v\n",v)}funcprintErr(prefixstring,errerror){iferr!=nil{msg:=errors.Details(err,nil)fmt.Printf("%s:\n%s\n",prefix,msg)}}funcloose(vcue.Value)error{returnv.Validate(// not final or concretecue.Concrete(false),// check minimallycue.Definitions(false),cue.Hidden(false),cue.Optional(false),)}funcevery(vcue.Value)error{returnv.Validate(// not final or concretecue.Concrete(false),// check everythingcue.Definitions(true),cue.Hidden(true),cue.Optional(true),)}funcstrict(vcue.Value)error{returnv.Validate(// ensure final and concretecue.Final(),cue.Concrete(true),// check everythingcue.Definitions(true),cue.Hidden(true),cue.Optional(true),)}
go run validate.go
loose error:
#d: conflicting values int and "bar" (mismatched types int and string):
7:5
8:5
_h: conflicting values int and "hidden" (mismatched types int and string):
5:5
6:5
every error:
#d: conflicting values int and "bar" (mismatched types int and string):
7:5
8:5
_h: conflicting values int and "hidden" (mismatched types int and string):
5:5
6:5
strict error:
#d: conflicting values int and "bar" (mismatched types int and string):
7:5
8:5
_h: conflicting values int and "hidden" (mismatched types int and string):
5:5
6:5
value:
i: int
s: string
t: {
[string]: string
}
_h: _|_ // conflicting values int and "hidden" (mismatched types int and string)
#d: _|_ // conflicting values int and "bar" (mismatched types int and string)
See issue 1329 for a discussion on Options,
as they do not seem to all be used. These docs will be updated once there is more clarification.
Value.Unify()
Unify is the & from your CUE code and combines two values in the same way.
It returns the result of the unification.
unify.go
packagemainimport("fmt""cuelang.org/go/cue""cuelang.org/go/cue/cuecontext""cuelang.org/go/cue/errors")constschema=`
v: {
i: int
s: string
}
`constval=`
v: {
i: "hello"
s: 1
}
`funcmain(){c:=cuecontext.New()s:=c.CompileString(schema,cue.Filename("schema.cue"))v:=c.CompileString(val,cue.Filename("val.cue"))// unify the schema and valueu:=s.Unify(v)// check for errors during unificationifu.Err()!=nil{msg:=errors.Details(u.Err(),nil)fmt.Printf("Unify Error:\n%s\n",msg)}// To get all errors, we need to validateerr:=u.Validate()iferr!=nil{msg:=errors.Details(err,nil)fmt.Printf("Validate Error:\n%s\n",msg)}// print ufmt.Printf("%#v\n",u)}
go run unify.go
Unify Error:
v.i: conflicting values int and "hello" (mismatched types int and string):
schema.cue:3:5
val.cue:3:5
Validate Error:
v.i: conflicting values int and "hello" (mismatched types int and string):
schema.cue:3:5
val.cue:3:5
v.s: conflicting values string and 1 (mismatched types string and int):
schema.cue:4:5
val.cue:4:5
v: {
i: _|_ // conflicting values int and "hello" (mismatched types int and string)
s: _|_ // conflicting values string and 1 (mismatched types string and int)
}
Subsumption is a powerful technique which compares the relation of two values within the lattice.
Subsume will tell you if one value is an instance of another,
or to say it another way, if one value is backwards compatible with another.
In the following example, we check subsume in both directions.
For each pair, we expect one to pass and one to fail.
subsume.go
packagemainimport("fmt""cuelang.org/go/cue""cuelang.org/go/cue/cuecontext""cuelang.org/go/cue/errors")constschemaWithNumber=`
{
i: number
s: string
}
`constschemaWithInt=`
{
i: int
s: string
}
`constconstraint=`
{
i: >10 // this will only be subsumed by number, not int
s: =~"^foo"
}
`constval=`
{
i: 100
s: "foobar"
}
`constconstraintType=`
{
i: uint
s: string
}
`funcmain(){ctx:=cuecontext.New()sn:=ctx.CompileString(schemaWithNumber,cue.Filename("schema_number.cue"))si:=ctx.CompileString(schemaWithInt,cue.Filename("schema_int.cue"))c:=ctx.CompileString(constraint,cue.Filename("constraint.cue"))v:=ctx.CompileString(val,cue.Filename("val.cue"))b:=ctx.CompileString(constraintType,cue.Filename("bad.cue"))// check subsumptionsprintErr("sn > c",sn.Subsume(c))printErr("c > sn",c.Subsume(sn))printErr("sn > v",sn.Subsume(v))printErr("v > sn",v.Subsume(sn))// this seems not intuitive, we'll talk it laterprintErr("si > c",si.Subsume(c))printErr("c > si",c.Subsume(si))printErr("si > v",si.Subsume(v))printErr("v > si",v.Subsume(si))printErr("s > b",si.Subsume(b))printErr("b > v",b.Subsume(v))}funcprintErr(prefixstring,errerror){iferr!=nil{msg:=errors.Details(err,nil)fmt.Printf("%s:\n%s\n",prefix,msg)}}
go run subsume.go
c > sn:
field i not present in {i:number,s:string}:
schema_number.cue:2:1
missing field "i"
v > sn:
field i not present in {i:number,s:string}:
schema_number.cue:2:1
missing field "i"
si > c:
field i not present in {i:>10,s:=~"^foo"}:
constraint.cue:2:1
missing field "i"
c > si:
field i not present in {i:int,s:string}:
schema_int.cue:2:1
missing field "i"
v > si:
field i not present in {i:int,s:string}:
schema_int.cue:2:1
missing field "i"
The reason is that >10 is a constraint on numbers, just like int.
Recall number is both decimal and integers.
This means both of our constraints are peers in the lattice.
We could have set i: int & >10 and this would be subsumed by int.
also note, that if the values were all in the same file,
the unification would have been implied.
Value.Eval()
Eval will resolve references, ensure concreteness, uncover all errors and
generally finalizes a value, returning the result in a new value.
eval.go
packagemainimport("fmt""cuelang.org/go/cue""cuelang.org/go/cue/cuecontext""cuelang.org/go/cue/errors")constval=`
v: {
i: int
s: "hello"
#d: "defn"
_h: "hidden"
o?: string
}
`funcmain(){c:=cuecontext.New()v:=c.CompileString(val,cue.Filename("val.cue"))// check for errors during compilingifv.Err()!=nil{msg:=errors.Details(v.Err(),nil)fmt.Printf("Compile Error:\n%s\n",msg)}// print vfmt.Printf("%#v\n",v)// eval evaluates and returns a new valuee:=v.Eval()ife.Err()!=nil{msg:=errors.Details(e.Err(),nil)fmt.Printf("Eval Error:\n%s\n",msg)}// print efmt.Printf("%#v\n",e)}