cue-lang / cue

The home of the CUE language! Validate and define text-based and dynamic configuration
https://cuelang.org
Apache License 2.0
5.09k stars 291 forks source link

References don't interact properly with braceless syntax sugar #93

Closed cueckoo closed 3 years ago

cueckoo commented 3 years ago

Originally opened by @masaeedu in https://github.com/cuelang/cue/issues/93

This:

outer: {
  middle1: { inner: 3 }
  middle2: { inner: middle1 }
}

evaluates to:

{
    "outer": {
        "middle1": {
            "inner": 3
        },
        "middle2": {
            "inner": {
                "inner": 3
            }
        }
    }
}

but this (if I understand the docs correctly) equivalent shorthand:

outer middle1 inner: 3
outer middle2 inner: middle1

explodes with:

outer.middle2.inner:reference "middle1" not found:
    ./config/services.cue:2:22
cueckoo commented 3 years ago

Original reply by @mpvl in https://github.com/cuelang/cue/issues/93#issuecomment-527638137

outer middle1 inner: 3
outer middle2 inner: middle1

is syntactic sugar for.

outer: { middle1: { inner: 3 } }
outer: { middle2: { inner: middle1 } }

So middle1 is not in scope where it is referred: CUE has strictly lexical binding. In other words, it is resolved similarly to most modern programming languages and before all the structs are expanded. If the reference cannot be resolved lexically, it will not be resolved later. So unlike GCL.

Would you have interpreted it differently if the syntax had been

outer.middle1.inner: 3
outer.middle2.inner: middle1

or

outer: middle1: inner: 3
outer: middel2: inner: middle1

? (see Issue #60).

For completeness,

outer: { middle1: { inner: 3 } }
outer: { middle2: { inner: 4 } }

evaluates in the following steps:

outer: { middle1: { inner: 3 } } & { middle2: { inner: 4 } }

then

outer: {
  middle1: { inner: 3 }
  middle2: { inner: 4 }
}

Closing as "not a bug", but feedback welcome.

cueckoo commented 3 years ago

Original reply by @masaeedu in https://github.com/cuelang/cue/issues/93#issuecomment-527651203

outer middle1 inner: 3
outer middle2 inner: middle1

is syntactic sugar for.

outer: { middle1: { inner: 3 } }
outer: { middle2: { inner: middle1 } }

Yes. Which in turn is (I mistakenly assumed) syntactic sugar for:

outer: { middle1: { inner: 3 }, middle2: { inner: middle1 } }

But given an expression:

foo bar: <x>
foo baz: <y>

it turns out whether this is mere syntax sugar for:

foo: { bar: <x>, baz: <y> }

is contingent on the contents of the subexpressions <x> and <y>.

cueckoo commented 3 years ago

Original reply by @mpvl in https://github.com/cuelang/cue/issues/93#issuecomment-529078436

@massaeedu It is not contingent on the contents. Duplicate field entries are defined to be treated as a conjunction. So

foo: a
foo: b

is evaluated as

foo: a & b

which is a normal expression, not sugar. If this is not written clearly in one of the documents that should be fixed.

But in general, with lexical scoping the rule is that a reference resolves to what is visible lexically as you can see it before any rewriting/ expansion of sugar. Ideally, expansion of sugar should not affect visibility, and that should be the case indeed. This is one of the reasons why references in RHS expressions can reference LHS labels (foo bar baz: bar.qux is legal, assuming qux is defined to be in bar elsewhere. Note that, CUE adopts the common convention where the RHS bar is a reference, while qux is a selector and thus qux is looked up in bar, and not resolved lexically. Go, C, Java, etc all work the same way. )

cueckoo commented 3 years ago

Original reply by @jba in https://github.com/cuelang/cue/issues/93#issuecomment-529096109

This would be helped slightly if the LHS spaces were replaced by dots, as has been suggested:

outer.middle1.inner: 3
cueckoo commented 3 years ago

Original reply by @mpvl in https://github.com/cuelang/cue/issues/93#issuecomment-529098668

@jba would it still be clear middle refers to the LHS middle in

outer.middle.inner: middle.a

When using dots? That seems a bit unintuitive to me (but may differ for others).

I find this clearer using colons:

outer: middle: inner: middle.a

Although that looks a bit more messy.