-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow type arguments to be tuple unpacked #429
Comments
This is something that I would like as well but generics and the mechanisms around them have to be considered carefully. Personally my idea was allowing for variadic typeargs and having some way of
honestly I don't think the implementation would be too hard (but don't quote me on that), I think the real issue is the ergonomics of the syntax. Perhaps one solution is to allow type-tuples in generics? This would allow for annotation of arguments and returns in generic arguments
Both of these are nice for when you want to just forward argument and return values for functions But it gets more complex with things like varargs, and just regular generics
should this be allowed? If it is should the arity of Or maybe the solution is something completely different. But I definitely think forwarding function arguments and returns is a common enough use case that at least some subset of it should be supported by the type system (also #211 is a slightly related but I think different issue) |
I would have thought the arity of
Also consider this (legal) lua snippet: local function foo(a,b,c)
return a + b + c
end
local function getargs()
return 1,2,3
end
print(foo(getargs())) Which prints Even though the arity of With teal generics this could look like: local function foo(a: number, b: number, c: number): number
return a + b + c
end
local function getargs(): number, number, number
return 1,2,3
end
local function apply<T, R>(a: (function(T): R), b: (function(): T)): R
return a(b())
end
apply(foo, getargs) -- T -> (number, number, number), and R -> number |
Lua does not always pass all the function results as-is. If the function call is not the last one in the list of expressions, Lua adjusts the number of returns to 1. For instance,
See https://www.lua.org/pil/5.1.html for reference. |
@overcrook correct, and Teal only expands the tuple of results if it's the last argument, otherwise it truncates it to arity 1, reproducing the expected Lua behavior. |
This is ugly, but it works: local async = {
-- dynamic implementation of await
await = function(defer: function(...: any): (any), ...:any): any...
return coroutine.yield(defer(...))
end
}
local record Async
type cb_function_1 = function<A>(function(A))
type cb_function_2 = function<A,B>(function(A,B))
type cb_function_3 = function<A,B,C>(function(A,B,C))
type cb_function_4 = function<A,B,C,D>(function(A,B,C,D))
type cb_function_5 = function<A,B,C,D,E>(function(A,B,C,D,E))
type cb_function_6 = function<A,B,C,D,E,F>(function(A,B,C,D,E,F))
type cb_function_7 = function<A,B,C,D,E,F,G>(function(A,B,C,D,E,F,G))
type cb_function_8 = function<A,B,C,D,E,F,G,H>(function(A,B,C,D,E,F,G,H))
type cb_function_9 = function<A,B,C,D,E,F,G,H,I>(function(A,B,C,D,E,F,G,H,I))
type cb_function_10 = function<A,B,C,D,E,F,G,H,I,J>(function(A,B,C,D,E,F,G,H,I,J))
type async_function_1 = function<A>(...: any): cb_function_1<A>
type async_function_2 = function<A,B>(...: any): cb_function_2<A,B>
type async_function_3 = function<A,B,C>(...: any): cb_function_3<A,B,C>
type async_function_4 = function<A,B,C,D>(...: any): cb_function_4<A,B,C,D>
type async_function_5 = function<A,B,C,D,E>(...: any): cb_function_5<A,B,C,D,E>
type async_function_6 = function<A,B,C,D,E,F>(...: any): cb_function_6<A,B,C,D,E,F>
type async_function_7 = function<A,B,C,D,E,F,G>(...: any): cb_function_7<A,B,C,D,E,F,G>
type async_function_8 = function<A,B,C,D,E,F,G,H>(...: any): cb_function_8<A,B,C,D,E,F,G,H>
type async_function_9 = function<A,B,C,D,E,F,G,H,I>(...: any): cb_function_9<A,B,C,D,E,F,G,H,I>
type async_function_10 = function<A,B,C,D,E,F,G,H,I,J>(...: any): cb_function_10<A,B,C,D,E,F,G,H,I,J>
-- polymorphic definition of await
await: function<A>(defer: async_function_1<A>, ...: any): A
await: function<A,B>(defer: async_function_2<A,B>, ...: any): A,B
await: function<A,B,C>(defer: async_function_3<A,B,C>, ...: any): A,B,C
await: function<A,B,C,D>(defer: async_function_4<A,B,C,D>, ...: any): A,B,C,D
await: function<A,B,C,D,E>(defer: async_function_5<A,B,C,D,E>, ...: any): A,B,C,D,E
await: function<A,B,C,D,E,F>(defer: async_function_6<A,B,C,D,E,F>, ...: any): A,B,C,D,E,F
await: function<A,B,C,D,E,F,G>(defer: async_function_7<A,B,C,D,E,F,G>, ...: any): A,B,C,D,E,F,G
await: function<A,B,C,D,E,F,G,H>(defer: async_function_8<A,B,C,D,E,F,G,H>, ...: any): A,B,C,D,E,F,G,H
await: function<A,B,C,D,E,F,G,H,I>(defer: async_function_9<A,B,C,D,E,F,G,H,I>, ...: any): A,B,C,D,E,F,G,H,I
await: function<A,B,C,D,E,F,G,H,I,J>(defer: async_function_10<A,B,C,D,E,F,G,H,I,J>, ...: any): A,B,C,D,E,F,G,H,I,J
end
return async as Async Yes, it looks like a terrible hack, but there is actually precedent for solutions like this in other languages. In usage: local async = require("async")
local function f(n: number): async.cb_function_2<boolean, string>
return function(): boolean, string
return true, "hello"
end
end
local ok, msg = async.await(f, 10)
if ok then
print(msg)
end |
I knew that link would point to the scala docs! Scala also defines all versions of Tuple this way: Thanks for the solution anyhow, it's certainly usuable. Would doing a non-hacky solution here by unpacking tuples (as lua does) be really that hard to implement? I.e. |
(To avoid confusion, what we're calling "tuples" here is what @euclidianAce called type-tuples above, declared as Tuples are a second-class type in Lua. It is the type of Similarly, they are a second-class type in Teal — it exists only inside the compiler to implement Lua semantics, but it's not presented to the user as a declarable type of its own. You're subject to the same limitations using Allowing such second-class type to be assigned to a type variable would have rippling effects, because a type variable is treated as a first-class type: you can declare variables with it, pass it around, assign it to If we were to have something like this, I think it would make more sense to add "variadic type variables" as in |
Thanks for the thorough explanation. Makes complete sense to me. I'll use the solution you've suggested in the meantime. |
For anyone interested I just stumbled upon this really well written blog post that comprehensively explains the second class nature of lua tuples (aka multivals) that is quite relevant to this ticket. |
The neovim async lib has now been merged and I've been able to enable type checking using the suggestion of specialising each kind of invocation. The API is a little different than my first comment but the principle is the same. In order to also type check arguments to an async function I've had to specialise on those too. This makes the resulting code a magnitude more ugly as we now need to speicalise across two dimensions: the input argument types and the return types. I needed to speciliase for a 4x4 matrix of these values however heres a version for 2x2: local a = require('plenary/async_lib/async')
local record M
type future0 = function(function())
type future1 = function<A1>(function(A1))
type future2 = function<A1,A2>(function(A1,A2))
await0: function(future0): ()
await1: function<A1>(future1<A1>): A1
await2: function<A1,A2>(future2<A1,A2>): A1,A2
type async_fun0_0 = function(): future0
type async_fun0_1 = function<R1>(): future1<R1>
type async_fun0_2 = function<R1,R2>(): future2<R1,R2>
type async_fun1_0 = function<A1>(A1): future0
type async_fun1_1 = function<A1,R1>(A1): future1<R1>
type async_fun1_2 = function<A1,R1,R2>(A1): future2<R1,R2>
type async_fun2_0 = function<A1,A2>(A1,A2): future0
type async_fun2_1 = function<A1,A2,R1>(A1,A2): future1<R1>
type async_fun2_2 = function<A1,A2,R1,R2>(A1,A2): future2<R1,R2>
wrap0_0: function(function(function())): async_fun0
wrap0_1: function<R1>(function(function(R1))): async_fun0_1<R1>
wrap0_2: function<R1,R2>(function(function(R1,R2))): async_fun0_2<R1,R2>
wrap1_0: function<A1>(function(A1,function())): async_fun1<A1>
wrap1_1: function<A1,R1>(function(A1,function(R1))): async_fun1_1<A1,R1>
wrap1_2: function<A1,R1,R2>(function(A1,function(R1,R2))): async_fun1_2<A1,R1,R2>
wrap2:_0 function<A1,A2>(function(A1,A2,function())): async_fun2<A1,A2>
wrap2_1: function<A1,A2,R1>(function(A1,A2,function(R1))): async_fun2_1<A1,A2,R1>
wrap2_2: function<A1,A2,R1,R2>(function(A1,A2,function(R1,R2))): async_fun2_2<A1,A2,R1,R2>
async0_0: function(function(): ()): async_fun0
async0_1: function<R1>(function(): R1): async_fun0_1<R1>
async0_2: function<R1,R2>(function(): R1,R2): async_fun0_2<R1,R2>
async1_0: function<A1>(function(A1): ()): async_fun1<A1>
async1_1: function<A1,R1>(function(A1): R1): async_fun1_1<A1,R1>
async1_2: function<A1,R1,R2>(function(A1): R1,R2): async_fun1_2<A1,R1,R2>
async2_0: function<A1,A2>(function(A1,A2): ()): async_fun2<A1,A2>
async2_1: function<A1,A2,R1>(function(A1,A2): R1): async_fun2_1<A1,A2,R1>
async2_2: function<A1,A2,R1,R2>(function(A1,A2): R1,R2): async_fun2_2<A1,A2,R1,R2>
end
M.await0 = a.await as function(M.future0): ()
M.await1 = a.await as function<A1>(M.future1<A1>): A1
M.await2 = a.await as function<A1,A2>(M.future2<A1,A2>): A1,A2
M.wrap0_0 = function(func: function(function())): M.async_fun0
return a.wrap(func, 1) as M.async_fun0
end
M.wrap0_1 = function<R1>(func: function(function(R1))): M.async_fun0_1<R1>
return a.wrap(func, 1) as M.async_fun0_1<R1>
end
M.wrap0_2 = function<R1,R2>(func: function(function(R1,R2))): M.async_fun0_2<R1,R2>
return a.wrap(func, 1) as M.async_fun0_2<R1,R2>
end
M.wrap1_0 = function<A1>(func: function(A1,function())): M.async_fun1<A1>
return a.wrap(func, 2) as M.async_fun1<A1>
end
M.wrap1_1 = function<A1,R1>(func: function(A1,function(R1))): M.async_fun1_1<A1,R1>
return a.wrap(func, 2) as M.async_fun1_1<A1,R1>
end
M.wrap1_2 = function<A1,R1,R2>(func: function(A1,function(R1,R2))): M.async_fun1_2<A1,R1,R2>
return a.wrap(func, 2) as M.async_fun1_2<A1,R1,R2>
end
M.wrap2_0 = function<A1,A2>(func: function(A1,A2,function())): M.async_fun2<A1,A2>
return a.wrap(func, 3) as M.async_fun2<A1,A2>
end
M.wrap2_1 = function<A1,A2,R1>(func: function(A1,A2,function(R1))): M.async_fun2_1<A1,A2,R1>
return a.wrap(func, 3) as M.async_fun2_1<A1,A2,R1>
end
M.wrap2_2 = function<A1,A2,R1,R2>(func: function(A1,A2,function(R1,R2))): M.async_fun2_2<A1,A2,R1,R2>
return a.wrap(func, 3) as M.async_fun2_2<A1,A2,R1,R2>
end
M.async0_0 = a.async as function(function(): ()): M.async_fun0
M.async0_1 = a.async as function<R1>(function(): R1): M.async_fun0_1<R1>
M.async0_2 = a.async as function<R1,R2>(function(): R1,R2): M.async_fun0_2<R1,R2>
M.async1_0 = a.async as function<A1>(function(A1): ()): M.async_fun1<A1>
M.async1_1 = a.async as function<A1,R1>(function(A1): R1): M.async_fun1_1<A1,R1>
M.async1_2 = a.async as function<A1,R1,R2>(function(A1): R1,R2): M.async_fun1_2<A1,R1,R2>
M.async2_0 = a.async as function<A1,A2>(function(A1,A2): ()): M.async_fun2<A1,A2>
M.async2_1 = a.async as function<A1,A2,R1>(function(A1,A2): R1): M.async_fun2_1<A1,A2,R1>
M.async2_2 = a.async as function<A1,A2,R1,R2>(function(A1,A2): R1,R2): M.async_fun2_2<A1,A2,R1,R2>
return M This was so tedious to write for 4x4 that I had to script it! There's also quite a lot of repitition in here that I'm not sure how to avoid (if it's even possible). This basically enables full typechecking with statements like: local get_repo_info = wrap1_3(function(path: string, callback: function(string,string,string))
...
end)
local cwd: string = ...;
local toplevel: string, gitdir: string, abbrev_head: string = await3(get_repo_info(cwd))
With variadic type variables I'd hope we could reduce the above to: local a = require('plenary/async_lib/async')
local record M
type future = function<A...>(function(A))
await: function<A...>(future<A>): A
type async_fun = function<A...,R...>(A): future<R>
wrap: function<A...,R...>(function(A,function(R))): async_fun<A,R>
-- ^ would this be possible?
async: function<A...,R...>(function(A): R): async_fun<A,R>
end
M.await1 = a.await as function<A...>(M.future<A>): A
M.wrap = function<A...,R...>(func: function(A, function(R)), argc: integer): M.async_fun<A,R>
return a.wrap(func, argc) as M.async_fun<A,R>
end
M.async = a.async as function<A...,R...>(function(A): R): M.async_fun<A,R>
return M Thoughts? I going to guess some of what I want here won't be achievable. |
For what it is worth: Rust has the same problem but luckily that has macro's to help, resulting in fun code like impl_teal_multi_value!();
impl_teal_multi_value!(A);
impl_teal_multi_value!(A B);
impl_teal_multi_value!(A B C);
impl_teal_multi_value!(A B C D);
impl_teal_multi_value!(A B C D E);
impl_teal_multi_value!(A B C D E F);
impl_teal_multi_value!(A B C D E F G);
impl_teal_multi_value!(A B C D E F G H);
impl_teal_multi_value!(A B C D E F G H I);
impl_teal_multi_value!(A B C D E F G H I J);
impl_teal_multi_value!(A B C D E F G H I J K);
impl_teal_multi_value!(A B C D E F G H I J K L);
impl_teal_multi_value!(A B C D E F G H I J K L M);
impl_teal_multi_value!(A B C D E F G H I J K L M N);
impl_teal_multi_value!(A B C D E F G H I J K L M N O);
impl_teal_multi_value!(A B C D E F G H I J K L M N O P); I'm however less sure on what the best solution is. I can see why teal needs a proper solution more than typescript, due to lua's ability to return multiple values. However at the same time, unpacking generics like this looks wild at first glance. Macro's are technically a sort of solution but, as you can see above, that doesn't look great either and macro's are a rabbit hole on their own as well. |
@lewis6991 thanks for the further feedback! yes, I have briefly thought "there's going to blow up huge if he tries to use if for both inputs and outputs"... Have you considered using the polymorphic definitions for the public API? That is, using the same function name for the various signatures (see @lenscas yeah, I'm leaning towards "second-class variadic type arguments" as I suggested above as a possible solution that would fit, but right now I see it as less of a priority compared to other things like interfaces. But I'm keeping this on the back of my mind; if these scenarios of libraries wrapping functions generically start to pop up more often, this could bump in priority. |
I did try this and bumped into issues. It wasn't correctly type checking arguments and returns, it didn't error but It also didn't error when it should. I think argument and return types for each function are being unionized incorrectly, e.g. It seems like: foo: function(string): number
foo: function(number): string Was being treated as: foo: function(string|number): number|string Which is not what I want as I'm probably over generalising here and the specific issues I had were a bit more nuanced so I'll investigate a bit more. |
Ok tried using polymorphic functions again and seems to work a treat without any issues. What I witnissed before must of been an error on my part. FInal types: local record Async
type future = function (function)
type future0 = function (function())
type future1 = function<A1> (function(A1))
type future2 = function<A1,A2> (function(A1,A2))
type future3 = function<A1,A2,A3> (function(A1,A2,A3))
type future4 = function<A1,A2,A3,A4>(function(A1,A2,A3,A4))
type async_fun0 = function () : future0
type async_fun0_1 = function <R1> () : future1<R1>
type async_fun0_2 = function <R1,R2> () : future2<R1,R2>
type async_fun0_3 = function <R1,R2,R3> () : future3<R1,R2,R3>
type async_fun0_4 = function <R1,R2,R3,R4> () : future4<R1,R2,R3,R4>
type async_fun1 = function <A1> (A1) : future0
type async_fun1_1 = function <A1,R1> (A1) : future1<R1>
type async_fun1_2 = function <A1,R1,R2> (A1) : future2<R1,R2>
type async_fun1_3 = function <A1,R1,R2,R3> (A1) : future3<R1,R2,R3>
type async_fun1_4 = function <A1,R1,R2,R3,R4> (A1) : future4<R1,R2,R3,R4>
type async_fun2 = function <A1,A2> (A1,A2) : future0
type async_fun2_1 = function <A1,A2,R1> (A1,A2) : future1<R1>
type async_fun2_2 = function <A1,A2,R1,R2> (A1,A2) : future2<R1,R2>
type async_fun2_3 = function <A1,A2,R1,R2,R3> (A1,A2) : future3<R1,R2,R3>
type async_fun2_4 = function <A1,A2,R1,R2,R3,R4> (A1,A2) : future4<R1,R2,R3,R4>
type async_fun3 = function <A1,A2,A3> (A1,A2,A3) : future0
type async_fun3_1 = function <A1,A2,A3,R1> (A1,A2,A3) : future1<R1>
type async_fun3_2 = function <A1,A2,A3,R1,R2> (A1,A2,A3) : future2<R1,R2>
type async_fun3_3 = function <A1,A2,A3,R1,R2,R3> (A1,A2,A3) : future3<R1,R2,R3>
type async_fun3_4 = function <A1,A2,A3,R1,R2,R3,R4> (A1,A2,A3) : future4<R1,R2,R3,R4>
type async_fun4 = function <A1,A2,A3,A4> (A1,A2,A3,A4) : future0
type async_fun4_1 = function <A1,A2,A3,A4,R1> (A1,A2,A3,A4) : future1<R1>
type async_fun4_2 = function <A1,A2,A3,A4,R1,R2> (A1,A2,A3,A4) : future2<R1,R2>
type async_fun4_3 = function <A1,A2,A3,A4,R1,R2,R3> (A1,A2,A3,A4) : future3<R1,R2,R3>
type async_fun4_4 = function <A1,A2,A3,A4,R1,R2,R3,R4> (A1,A2,A3,A4) : future4<R1,R2,R3,R4>
await: function (future0 ): ()
await: function<A1> (future1<A1> ): A1
await: function<A1,A2> (future2<A1,A2> ): A1,A2
await: function<A1,A2,A3> (future3<A1,A2,A3> ): A1,A2,A3
await: function<A1,A2,A3,A4> (future4<A1,A2,A3,A4>): A1,A2,A3,A4
async: function (function() : () ): async_fun0
async: function<R1> (function() : R1 ): async_fun0_1 <R1>
async: function<R1,R2> (function() : R1,R2 ): async_fun0_2 <R1,R2>
async: function<R1,R2,R3> (function() : R1,R2,R3 ): async_fun0_3 <R1,R2,R3>
async: function<R1,R2,R3,R4> (function() : R1,R2,R3,R4): async_fun0_4 <R1,R2,R3,R4>
async: function<A1> (function(A1) : () ): async_fun1 <A1>
async: function<A1,R1> (function(A1) : R1 ): async_fun1_1 <A1,R1>
async: function<A1,R1,R2> (function(A1) : R1,R2 ): async_fun1_2 <A1,R1,R2>
async: function<A1,R1,R2,R3> (function(A1) : R1,R2,R3 ): async_fun1_3 <A1,R1,R2,R3>
async: function<A1,R1,R2,R3,R4> (function(A1) : R1,R2,R3,R4): async_fun1_4 <A1,R1,R2,R3,R4>
async: function<A1,A2> (function(A1,A2) : () ): async_fun2 <A1,A2>
async: function<A1,A2,R1> (function(A1,A2) : R1 ): async_fun2_1 <A1,A2,R1>
async: function<A1,A2,R1,R2> (function(A1,A2) : R1,R2 ): async_fun2_2 <A1,A2,R1,R2>
async: function<A1,A2,R1,R2,R3> (function(A1,A2) : R1,R2,R3 ): async_fun2_3 <A1,A2,R1,R2,R3>
async: function<A1,A2,R1,R2,R3,R4> (function(A1,A2) : R1,R2,R3,R4): async_fun2_4 <A1,A2,R1,R2,R3,R4>
async: function<A1,A2,A3> (function(A1,A2,A3) : () ): async_fun3 <A1,A2,A3>
async: function<A1,A2,A3,R1> (function(A1,A2,A3) : R1 ): async_fun3_1 <A1,A2,A3,R1>
async: function<A1,A2,A3,R1,R2> (function(A1,A2,A3) : R1,R2 ): async_fun3_2 <A1,A2,A3,R1,R2>
async: function<A1,A2,A3,R1,R2,R3> (function(A1,A2,A3) : R1,R2,R3 ): async_fun3_3 <A1,A2,A3,R1,R2,R3>
async: function<A1,A2,A3,R1,R2,R3,R4> (function(A1,A2,A3) : R1,R2,R3,R4): async_fun3_4 <A1,A2,A3,R1,R2,R3,R4>
async: function<A1,A2,A3,A4> (function(A1,A2,A3,A4) : () ): async_fun4 <A1,A2,A3,A4>
async: function<A1,A2,A3,A4,R1> (function(A1,A2,A3,A4) : R1 ): async_fun4_1 <A1,A2,A3,A4,R1>
async: function<A1,A2,A3,A4,R1,R2> (function(A1,A2,A3,A4) : R1,R2 ): async_fun4_2 <A1,A2,A3,A4,R1,R2>
async: function<A1,A2,A3,A4,R1,R2,R3> (function(A1,A2,A3,A4) : R1,R2,R3 ): async_fun4_3 <A1,A2,A3,A4,R1,R2,R3>
async: function<A1,A2,A3,A4,R1,R2,R3,R4>(function(A1,A2,A3,A4) : R1,R2,R3,R4): async_fun4_4 <A1,A2,A3,A4,R1,R2,R3,R4>
wrap: function (function( function()) , integer): async_fun0
wrap: function<R1> (function( function(R1)) , integer): async_fun0_1 <R1>
wrap: function<R1,R2> (function( function(R1,R2)) , integer): async_fun0_2 <R1,R2>
wrap: function<R1,R2,R3> (function( function(R1,R2,R3)) , integer): async_fun0_3 <R1,R2,R3>
wrap: function<R1,R2,R3,R4> (function( function(R1,R2,R3,R4)), integer): async_fun0_4 <R1,R2,R3,R4>
wrap: function<A1> (function(A1, function()) , integer): async_fun1 <A1>
wrap: function<A1,R1> (function(A1, function(R1)) , integer): async_fun1_1 <A1,R1>
wrap: function<A1,R1,R2> (function(A1, function(R1,R2)) , integer): async_fun1_2 <A1,R1,R2>
wrap: function<A1,R1,R2,R3> (function(A1, function(R1,R2,R3)) , integer): async_fun1_3 <A1,R1,R2,R3>
wrap: function<A1,R1,R2,R3,R4> (function(A1, function(R1,R2,R3,R4)), integer): async_fun1_4 <A1,R1,R2,R3,R4>
wrap: function<A1,A2> (function(A1,A2, function()) , integer): async_fun2 <A1,A2>
wrap: function<A1,A2,R1> (function(A1,A2, function(R1)) , integer): async_fun2_1 <A1,A2,R1>
wrap: function<A1,A2,R1,R2> (function(A1,A2, function(R1,R2)) , integer): async_fun2_2 <A1,A2,R1,R2>
wrap: function<A1,A2,R1,R2,R3> (function(A1,A2, function(R1,R2,R3)) , integer): async_fun2_3 <A1,A2,R1,R2,R3>
wrap: function<A1,A2,R1,R2,R3,R4> (function(A1,A2, function(R1,R2,R3,R4)), integer): async_fun2_4 <A1,A2,R1,R2,R3,R4>
wrap: function<A1,A2,A3> (function(A1,A2,A3, function()) , integer): async_fun3 <A1,A2,A3>
wrap: function<A1,A2,A3,R1> (function(A1,A2,A3, function(R1)) , integer): async_fun3_1 <A1,A2,A3,R1>
wrap: function<A1,A2,A3,R1,R2> (function(A1,A2,A3, function(R1,R2)) , integer): async_fun3_2 <A1,A2,A3,R1,R2>
wrap: function<A1,A2,A3,R1,R2,R3> (function(A1,A2,A3, function(R1,R2,R3)) , integer): async_fun3_3 <A1,A2,A3,R1,R2,R3>
wrap: function<A1,A2,A3,R1,R2,R3,R4> (function(A1,A2,A3, function(R1,R2,R3,R4)), integer): async_fun3_4 <A1,A2,A3,R1,R2,R3,R4>
wrap: function<A1,A2,A3,A4> (function(A1,A2,A3,A4,function()) , integer): async_fun4 <A1,A2,A3,A4>
wrap: function<A1,A2,A3,A4,R1> (function(A1,A2,A3,A4,function(R1)) , integer): async_fun4_1 <A1,A2,A3,A4,R1>
wrap: function<A1,A2,A3,A4,R1,R2> (function(A1,A2,A3,A4,function(R1,R2)) , integer): async_fun4_2 <A1,A2,A3,A4,R1,R2>
wrap: function<A1,A2,A3,A4,R1,R2,R3> (function(A1,A2,A3,A4,function(R1,R2,R3)) , integer): async_fun4_3 <A1,A2,A3,A4,R1,R2,R3>
wrap: function<A1,A2,A3,A4,R1,R2,R3,R4>(function(A1,A2,A3,A4,function(R1,R2,R3,R4)), integer): async_fun4_4 <A1,A2,A3,A4,R1,R2,R3,R4>
end
return Async I can also implement this as a Though still ugly, I'm much happier with this. Thanks! |
I've bumped into an issue with polymorphism of the async: function (function(): () ): async_fun0
async: function<R1> (function(): R1 ): async_fun0_1 <R1>
async: function<R1,R2>(function(): R1,R2): async_fun0_2 <R1,R2> All appear to be treated the same which results in the error with: toplevel, gitdir, abbrev_head = await(get_repo_info(file))
-- only 1 value is returned by this function
-- variable is not being assigned a value This is unlike wrap which works perfectly as it has the return types encoded into the callback argument. Is this a design feature (return type erasure) or can we consider this a bug? |
@lewis6991 Please try reordering the polymorphic definitions from "most specific" to "least specific" and let me know if it solves the issue! The ordering in polymorphic definitions are relevant. |
Thanks, that's solved it! |
Hmm for |
Context
I currently use teal for my neovim plugin and I'm trying to better annotate a small async library I've implemented as part of the plugin which uses lua co-routines.
There is also a more standard neovim async lib in development (in lua) which I would like to port (or at least provide type annotations) in teal.
Issue
Here's a relatively sanitized snippet of what I'm roughly trying to do. Basically I have a type argument
T
that I would like to be unpacked to represent the argument list of a function.Is this something that can be supported? My only workaround for the time being is to use arrays and varargs:
Many thanks!
The text was updated successfully, but these errors were encountered: