teal-language / tl

The compiler for Teal, a typed dialect of Lua
MIT License
2.02k stars 101 forks source link

Explicit generic function arguments #724

Open svermeulen opened 6 months ago

svermeulen commented 6 months ago

Sometimes, the type that is used by a generic function is not passed in implicitly by the arguments. These situations would benefit from having a way to explicitly tell the teal compiler what type should be used for a given generic argument.

For eg: local foo = get_foo::<integer>()

This was previously mentioned as something to consider here but couldn't find an issue for it (if I missed this please close)

hishamhm commented 6 months ago

Teal does bidirectional type inference to try to avoid the need for this.

In your example above, doing local foo: integer = get_foo() should make the expected type flow into the function call.

Do you have a more complex example where that doesn't work and the explicit generic function argument would still be necessary? I've been trying to see how far we can go without adding more syntax for function calls.

svermeulen commented 6 months ago

So I went back to the case that prompted this request, and found that I was able to avoid the need for an explicit generic by adding more locals with explicit types. There might be some cases where this workaround isn't sufficient but I can't find it at the moment.

hishamhm commented 6 months ago

There might be some cases where this workaround isn't sufficient but I can't find it at the moment.

I honestly don't see it as a workaround, since it amounts to the same number of annotations. I understand that one gets the feeling that ideally no variables should need to be annotated, but if anything, I find that not needing a new (pseudo-?)operator is a cleaner approach, language-wise.

Explicit variable annotations and bidirectional inference do a lot of heavy lifting to resolve the types of things like literal tables (e.g. local t: MyComplexRecord = { ... { ... { ... deeply_nested_thing = ... } ... } ... }), and this was the primary reason why we added bidirectional inference. Being able to use the same machinery to avoid the need for the "turbofish annotation" felt like a good way to go. The present rule is that variables for function calls are resolved either by the expected types of its return values in the given call-site context, or the types of its given arguments.