-
-
Notifications
You must be signed in to change notification settings - Fork 351
[feat] generics: capture types with backticks #3149
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
base: master
Are you sure you want to change the base?
Conversation
I think there is a big problem here: 😕
just using your example above ---@type Vehicle|nil
local v1 = {}
local r1 = getItemColor(v1) --> r1: VehicleColor|nilColor PS: I am not maintainer of LuaLS, just one of the contributors. |
No problem, I will review that case and update the PR |
Hello @tomlau10 . I looked at this after your feeback. I would leave the PR as-is. This indeed can end up creating weird types, but when you are using string you can also do weird stuff, namely, you can call I have another variant of the code that works as follows: ---@type Vehicle|nil
local v1 = {}
local r1 = getItemColor(v1) --> r1: VehicleColor|nil Meaning, Let me know which one would work best. I am fine either way. If not, what would you expect in such cases? My use case is, I am writing Lua bindings for a major ECS, we need a syntax as follows: ---@class transform.Position : Component
---@class transform.PositionInstance
---@field x number
---@field y number
-- Retrieves the provided component value or nil if the component is not found
---@generic T
---@param component `T`Instance # component to retrieve
---@return T | nil
function entity:get(component) end
local pos = entity:get(transform.Position) --> pos: transform.PositionInstance|nil
if pos then
print("Position: ", pos.x, pos.y)
else
print("Entity does not have Position")
end Please let me know your thoughts. |
When using literal string, I don't think anyone would write Moreover, the current implementation would give inconsistent result in some of the following cases: -- 1. just a variable with no annotation
local v1 --> v1: unknown
local r1 = getItemColor(v1) --> unknown, expected, but out of my expectation that it works correctly
-- 2. a table value
local v2 = {} --> v2: table
local r2 = getItemColor(v2) --> unknown, expected, again out of my expectation that it works correctly
-- 3. nil from function
local function getnil() end
local v3 = getnil() --> v3: nil
local r3 = getItemColor(v3) --> nilColor, unexpected I think
-- 4. integer const from function
local function get1() return 1 end
local v4 = get1() --> v4: integer = 1
local r4 = getItemColor(v4) --> unknown, but I don't know what should be the behavior
-- 5. integer from function
---@return integer
local function getint() return 1 end
local v5 = getint() --> v5: integer
local r5 = getItemColor(v5) --> integerColor, seems inconsistent with const integer case
-- 6. any from function
---@return any
local function getany() return end
local v6 = getany()
local r6 = getItemColor(v6) --> anyColor
I personally think skipping Again I am not author of LuaLS, so this may need opinions from maintainers. 🤔 |
Maybe we could get something like: ---@generic T
---@param x `T`
---@return `T`Colour
local function f(x)
return ...
end |
Since there is a And if |
I believe that too many peculiar syntaxes have been added to sumneko for various workarounds. This is neither consistent nor does it add cognitive burden. I think a better syntax would be to use built-in special generic classes based on supporting generics. ---@generic T
---@param a T
---@return CompositeType<T, "CCC">
function f(a)
end Of course, this is just a suggestion. I understand that implementing this in LuaLS might be somewhat difficult. |
If there is no exact planning on the syntax of how
|
I agree this is the syntax that would make sense, but it is not how the backticks capture works right now unfortunately. @CppCXY @tomlau10 So I agree with @tomlau10 :
... but adding the extra that ---@class transform.Position : Component
---@class transform.PositionInstance
---@field x number
---@field y number
-- Retrieves the provided component value or nil if the component is not found
---@generic T
---@param component `T`Instance # component to retrieve
---@return T | nil # T is actually `T`Instance
function entity:get(component) end
local pos = entity:get(transform.Position) --> pos: transform.PositionInstance|nil
if pos then
print("Position: ", pos.x, pos.y)
else
print("Entity does not have Position")
end |
Yes, that's what I mean 😄
|
e1ac726
to
e40a933
Compare
This PR modifies the generics capture system to allow to capture types out of the type of the parameter passed, in addition to being able to pass the type as a string which is the way it currently works:
Currently:
Now, the following is also possible:
Therefore, it is possible to pass a literal string that happens to be a type name like before, or otherwise pass an expression that evaluates to a type and that type will be captured with backticks. This allows to construct typings for functions that return different types that depend on the type of the object passed.