Open cueckoo opened 3 years ago
Original reply by @jlongtine in https://github.com/cuelang/cue/issues/69#issuecomment-516921341
@matthewmueller I think some work has been done on Protobuf: https://github.com/cuelang/cue/tree/master/encoding/protobuf
@mpvl would have a better idea of the state of that code, though.
Original reply by @mpvl in https://github.com/cuelang/cue/issues/69#issuecomment-517227475
@matthewmueller This is not planned at the moment, although I have given it some thought. The main reason not though is that I didn't have a concrete use case and I'm not sure how that would look like.
But as @jlongtine said, the infrastructure for Proto parsing and conversion is already in place. So it would be useful to know what exactly expect to do with such a feature.
Original reply by @mpvl in https://github.com/cuelang/cue/issues/69#issuecomment-533706195
@matthewmueller if you have a concrete example of how you would want to be using CUE this way I would be quite interested.
Original reply by @matthewmueller in https://github.com/cuelang/cue/issues/69#issuecomment-533729137
Hey @mpvl! I'm imagining an implementation of protobuf's generators that adds constraint-based validation.
Tons of unknowns here, but conceptually I was thinking something like this:
user.cue
email: "\w+@\w+\.\w{2,}" // some regex for email
CreateInput: {
email: email
password: string // perhaps @len(string) > 0 <= 30
born: >= 1900 <= 2019
}
CreateOutput: {
id: >=0
email: email
born: >= 1900 <= 2019
}
UpdateInput: {
email?: email
password?: string
born?: >= 1900 <= 2019
}
UpdateOutput: {
ok: true | false
}
User: {
create(in: CreateInput): CreateOutput
update(in: UpdateInput): UpdateOutput
}
Then, run cue generate user.cue --out cue/user.go
. This will create cue/user.go
. This file will contain a User struct (or maybe just an interface?) and a Marshal
and an Unmarshal
. The Marshal
and Unmarshal
bake in constraint-based validation.
Usage would look something like this:
main.go
package main
import "cue/user"
func main() {
buf, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
var u user.CreateInput
if err := user.Create.Unmarshal(buf, &u); err != nil {
panic(err)
}
fmt.Println("got a user.CreateInput!", u)
}
@neelance do you have some thoughts on this? Our discussion sparked this initial issue 😊
Original reply by @mpvl in https://github.com/cuelang/cue/issues/69#issuecomment-739228231
Just to add some thoughts here: with the upcoming proposal to generalize and harmonize the syntax, something that gets close to the above rpc signatures is:
create: {in: CreateInput}: CreateOutput // POSSIBLE NEW NOTATION FOR RPC DEFINITIONS
or maybe
create: {in: CreateInput} :: CreateOutput
(The scanner still recognizes ::
, even though it is unused)
Especially the former would require a minimal syntactic adjustment and generalizes, syntactically, what expressions are allowed as a label.
We are also considering allowing the call syntax for structs. Consider, this,
foo: {a: int, b: int, out: a + b } // struct with embedded scalar
call: (foo&{a: 1, b: 2}).out // 3
On tip, which supports embedded scalars, this can be written as:
foo: {#a: int, #b: int, #a + #b } // struct with embedded scalar
call: foo&{_, #a: 1, #b: 2} // 3
The ideas is to allow an extension of the builtin syntax for structs as well, allowing
foo: { #a: int, #b: int, #a + #b } // struct with embedded scalar
call: foo(#a: 1, #b: 2) // 3
Where foo(#a: 1, #b: 2)
is a shorthand for foo&{#a: 1, #b: 2}
, with the additional requirement that #a
and #b
MUST be defined in foo
, even if foo
is an open struct.
This would also allow builtins to have Swift-style named arguments, which would allow some things to be written clearly, like:
range(from: 1, to: <5, by: 1)
Conversely, we could consider unnamed arguments for structs to map to the fields with name #0
, #1
, etc. Note that these are illegal identifiers right now to allow introducing this without causing backwards incompatibility.
These extensions to the call syntax also suggest the following possible syntax for rpc calls:
create: (in: CreateInput): CreateOutput
The problem with this is that there is an ambiguity with the proposed syntax (#165) for computed labels, especially if we want to allow unnamed arguments. A possible solution for this is to repurpose ::
:
create: (in: CreateInput) :: CreateOutput // ALTERNATIVE NEW NOTATION FOR RPC DEFINITIONS
This would remove that ambiguity and this notation has precedence in functional languages. A disadvantage of this approach is that it introduces more syntax (although limited). An advantage of doing so, though, is that it also would allow named return arguments without complications:
create: (in: CreateInput) :: (out: CreateOutput)
Another advantage of using ::
here is also that it makes it clearer that this is not defining data.
Note that with both proposed syntaxes, the existing field syntax would allow attributes to be defined along the arguments. This means it allows attributing additional meaning to arguments that are irrelevant to CUE, such as:
create: (in: CreateInput @grpc(stream)) :: (out: CreateOutput @grpc(stream))
or
create: {in: CreateInput @grpc(stream)}: {out: CreateOutput @grpc(stream)}
So so far, the best candidates are either of the form {}: {}
or () :: ()
, where in both cases the return value can be a normal expression. The {}: {}
form will require less additional syntax, but may be too indistinguishable from normal data. The () :: ()
form requires more additional syntax, but stands out more and also allows writing unnamed signatures like int :: int
.
Thoughts welcome.
Original reply by @verdverm in https://github.com/cuelang/cue/issues/69#issuecomment-739412692
I'm in favor of the () :: ()
syntax for its visual standout properties. This will help with readability, especially when I revisit code after a long time away, and likely easier for new users to understand what is going on. The call syntax as it is today can be confusing at first.
Originally opened by @matthewmueller in https://github.com/cuelang/cue/issues/69
I'm very interested in CUE for inter process communication. I was wondering if there are any plans to add function signatures to enforce how two processes can communicate with each other?
Something like services in Thrift / Protobuf or queries & mutations in a GraphQL schema.
This seems a bit outside of the core motivation for CUE, but it does seem like CUE would fit very well here too.