Open nguerrera opened 2 years ago
I am worried about establishing yet another naming convention, in part because it makes things more confusing in projection-or-cadl-function land. We're potentially looking at 4 classes of functions IIUC:
Parent
.getAddedOn
.For syntax options for declaring 2 (and maybe 3) I think we should consider alignment with future function and decorator declaration syntax. E.g. I would expect something like this to work:
fn @parent(parentType) {
self.state.set("parentType", parentType);
}
fn getParent(type) {
return type.state.get("parentType");
}
alias Parent<T> = getParent(T);
(And maybe the alias form ensures instantiation caching happens on the cadl side? I dunno)
There are a couple more options we could consider (not mutually exclusive):
cadlMain
It's up to the library author to split their implementation code such that they only export things into cadl that they intend to be called from cadl.
If a function doesn't have a namespace, it doesn't get bound. Not sure if it's ever good to have an un-namespaced function so this is maybe ok?
Any function which takes program (or later, context) as first parameter is bound in cadl. A quick-and-dirty regexp on the toString of the function may be sufficient here?
Great points.
I think requiring that Cadl callable things have a namespace is reasonable no matter what else we do. It seems like we would never recommend putting such things outside a Cadl namespace and unlike in .cadl, I have no real concern about adding one line of boilerplate to get started with JS interop.
I also think binding on signature is reasonable. I would really like to avoid regexes though. I think maybe it makes sense to go after signature help first so that we have more typing metadata available about functions and decorators. It's possible that with good typing we won't clutter tooling with option 1 (functions in type references) as we could only show functions that return a type in type reference completion. And then alias = as the way to cache the call and give it a nice type name seems fine. We can further use this typing to make it an error to call a function that doesn't return a type in a type reference.
So that's how I'm now leaning. It seems the most expressive. And maybe if we go after signatures first then it will be clearer if that's the right direction. I've been meaning to write up some options for decorator and function signatures since the dev doc proposal left that as a follow-up design discussion to be had. I'll try to have that up soon.
Funny, I thought 5 was the simplest and might end up obviating other approaches.
Maybe I don't understand, but 5 seems like something that helps fix the completion cluttering, but isn't a full alternative. It still needs to be paired with something like 1, 2 or 3 so that I have a way to invoke the functions for these scenarios, right?
I may be over-reacting to the current mess of seeing all helper functions, but it still would seem valuable to me if we didn't offer to complete functions that don't return types in a context where only a type can go. That said, that is harder. Maybe we can try 1 + 5, and then use typing later on when it comes online for signature help to make completion smarter?
I kind of like option 2 combined with some of Brian's suggestions, but I was afraid this was "let's definite Typescript functions in cadl" which it's not, so I'm okay with any outcome. Definitely seems like this would be useful, and I agree that the cluttering of helper functions is very unfortunate.
See https://github.com/microsoft/cadl/issues/462
Here we have a problem that KeysOf and ParentKeysOf are distinct types that we process twice. It would be nice if we could write:
However, we don't have currently have a way to author
Parent<T>
. We have a JS function exportedgetParentResource
but we don't have a way to call it. It would be nice if we somehow could.Brainstormed options
1. Allow calling exported functions in type references.
This seems very powerful, but I have some concerns:
2. Allow calling exported functions using template instantiation syntax
Here the
< >
parses this as a template instantiation, and when we resolvegetParentResource
to a function, we call it with the type arguments, and cache it like any other template instantiation. This solves the caching, but not the completion noise.3. Explicit template to function bridge
I struggled with the syntax here so consider this a straw-man. I want something that is different than
alias =
. I first hadmodel is
but realized it would be useful if this could return a non-model, and then realized it really is equivalent to a templated alias! But if we usealias =
we are really back to (1). The idea here is that you can make a 1:1 between an alias and a function. So I landed atalias is
but I don't like it.But syntax aside this solves the intellisense pollution and allows for idiomatic type (Pascal cased, noun) and function (camel case, verb) names.
4. Explicit opt-in of functions as templates
Here we borrow from decorators and have some way to opt a function into becoming a template on the Cadl side. Straw man:
$
is a decorator$$
is a template. This also solves the intellisense pollution. It could further allow one fewer declaration if you imagine havinggetParentResource
implementation inlined into$$Parent
.