lua-language-server icon indicating copy to clipboard operation
lua-language-server copied to clipboard

Type inferencing of filter style functions

Open lewis6991 opened this issue 2 years ago • 4 comments

Given:


---@generic T
---@param f fun(a: T)
---@param t table<any, T>
---@return T[] (table)
local function tbl_filter(f, t)
  return t
end

---@type string[]
local s = {'a', 'b', 'c'}

local r1 = tbl_filter(function(a) end, s)

r1 is inferred as unknown[] because a is unknown despite s being known. However, could we infer a from s and force the typechecker to insist a is a string?

If I remove the type from a:

---@generic T
---@param f fun(a)
---@param t table<any, T>
---@return T[] (table)
local function tbl_filter2(f, t)
  return t
end

local r2 = tbl_filter2(function(_) end, s)

r2 is now inferred as string[] because only s is used to infer the type.

lewis6991 avatar Feb 06 '23 11:02 lewis6991

I don't know how to design a strict rule.

sumneko avatar Feb 06 '23 11:02 sumneko

  • Look at all the sources of T
  • Make sure all the known sources match
  • For any unknown sources which come anonymous function arguments (or return values) insist it is the same type from the known sources.

Would that work?

lewis6991 avatar Feb 06 '23 11:02 lewis6991

I will try it, thank you.

sumneko avatar Feb 06 '23 11:02 sumneko

Interestingly, this does work as expected when using list T[] instead of table<any,T> (using version 3.7.3)

code:

---Filter a list
---@generic T
---@param list T[]
---@param p fun(v:T):boolean
---@return T[]
function iter.filter(list, p)
	local filtered = {}
	for _, v in ipairs(list) do
		if p(v) then
			table.insert(filtered, v)
		end
	end
	return filtered
end

-- number[]
local input = { 1, 2, 3, 4, 5 }

local filtered = iter.filter(input, function(v)
    return v % 2 == 0
end)

Screenshots of inferred types

filtered array: Screen Shot 2023-12-23 at 2 12 55 PM

filter function param: Screen Shot 2023-12-23 at 2 13 28 PM

itsfrank avatar Dec 23 '23 22:12 itsfrank