Skip to content

Conversation

@georgiy-belyanin
Copy link
Contributor

@georgiy-belyanin georgiy-belyanin commented Jan 30, 2025

This patch adds simple type level matching for the doc functions and signatures allowing to instantiate generics based on the passed functions.

The following code demonstrates a generic type instantiated by this feature.

---@param a number
---@return string
local function tostr(a)
    return tostring(a)
end

---@generic T, U
---@param x T
---@param f fun(x: T): U
---@return U
local function apply(x, f)
    return f(x)
end

--"a" is inferred as a string.
a = apply(3, tostr)

@georgiy-belyanin
Copy link
Contributor Author

georgiy-belyanin commented Jan 30, 2025

I hope the solution is acceptable as a temporary one.

I've a problematic scenario that's hard to deal with.
It'd nice to support the following annotation instantiation allowing really complex calls.

---@generic K, V
---@class fun.table_iterator<K, V>
table_iterator_methods = {}

---@generic K, V, W
---@param self fun.table_iterator<K, V>
---@param f fun(a: V): W
---@return fun.table_iterator<K, W>
function table_iterator_methods.map(self, f) end

---@type fun(a:string, b:string): number
local process = function(a, b)
    return tostring(b)
end

---@type fun.table_iterator<string, string>
local a = {}
-- The  `fun.table_iterator<string, number>` type is not inferred.
-- It's `fun.table_iterator<string, unknown>` instead.
local b = a:map(process)

It's impossible yet to infer the type of b. It happens due to the fact indexing a generic class relies on the fact it's possible to instantiate the type of the call only by instantiating the object generics.

Thus, currently the algorithm works in this steps.

a:map
* Infer type of A (fun.table_iterator<string, string>)
* Try to infer the type of A.map substituting `K` and `V` (and not substituting the arguments) 
* Got something like `fun(self, fun(a: string): unknown): fun.table_iterator<string, unknown>`.

Please, let me now if this is wrong or not.

georgiy-belyanin added a commit to georgiy-belyanin/emmylua-analyzer-rust that referenced this pull request Jan 30, 2025
This patch bumps `actions/upload-artifact@v3` to `actions/upload-artifact@v4`.
Now `v3` is deprecated [^1] which turned out to break EmmyLuaLs#9.

[^1] https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/
This patch bumps `actions/upload-artifact@v3` to `actions/upload-artifact@v4`.
Now `v3` is deprecated [^1] which turned out to break EmmyLuaLs#9.

[^1] https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/
This patch adds simple type level matching for the doc functions
and signatures allowing to instantiate generics based on the passed
functions.

The following code demonstrates a generic type instantiated by this
feature.
```lua
---@param a number
---@return string
local function tostr(a)
    return tostring(a)
end

---@Generic T, U
---@param x T
---@param f fun(x: T): U
---@return U
local function apply(x, f)
    return f(x)
end

--"a" is inferred as a string.
a = apply(3, tostr)
```
@CppCXY
Copy link
Member

CppCXY commented Jan 30, 2025

I think it might be better if you write it this way.
image

@CppCXY
Copy link
Member

CppCXY commented Jan 30, 2025

Generic functions are best defined this way, but there are currently some bugs that I will try to fix.
image

@CppCXY CppCXY merged commit b4f47e5 into EmmyLuaLs:main Jan 30, 2025
17 checks passed
@CppCXY
Copy link
Member

CppCXY commented Jan 30, 2025

I have fixed the issue with the generics, you can try the most reasonable way to write it again.

@georgiy-belyanin
Copy link
Contributor Author

georgiy-belyanin commented Jan 30, 2025

Thank you for an explanation and for working in it.

I've changed the return type of process to table and it works strangely. I'd appreciate if you check the scenario out when you have enough time. The type W is infered the same as K.

---@class fun.table_iterator<K, V>
table_iterator_methods = {}

---@generic W
---@param f fun(a: any): W
---@return fun.table_iterator<K, W>
function table_iterator_methods:map(f) end

---@param a string
---@param b number
---@return table
local function process(a, b)
    return {}
end

---@type fun.table_iterator<string, number>
local a = {}

-- b is fun.table_iterator<string, string>, not
-- fun.table_iterator<string, table>
local b = a:map(process)

The behavior is the same if you define process using local process = function ... syntax:

---@param a string
---@param b number
---@return table
local process = function(a, b)
    return {}
end

The syntax suggested by me also ends up with unknown type.

@CppCXY
Copy link
Member

CppCXY commented Jan 30, 2025

Thank you for an explanation and for working in it.

I've changed the return type of process to table and it works strangely. I'd appreciate if you check the scenario out when you have enough time. The type W is infered the same as K.

---@class fun.table_iterator<K, V>
table_iterator_methods = {}

---@generic W
---@param f fun(a: any): W
---@return fun.table_iterator<K, W>
function table_iterator_methods:map(f) end

---@param a string
---@param b number
---@return table
local function process(a, b)
    return {}
end

---@type fun.table_iterator<string, number>
local a = {}

-- b is fun.table_iterator<string, string>, not
-- fun.table_iterator<string, table>
local b = a:map(process)

The behavior is the same if you define process using local process = function ... syntax:

---@param a string
---@param b number
---@return table
local process = function(a, b)
    return {}
end

The syntax suggested by me also ends up with unknown type.

I roughly understand why — there are two generic parameters from different sources here, both numbered 0. I might need to change the generics to an enum type. I'll fix this after I wake up.

@CppCXY
Copy link
Member

CppCXY commented Jan 31, 2025

I have fix this issue, please try again

@georgiy-belyanin
Copy link
Contributor Author

It now works properly. Thank you very much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants