same everywhere. Scopes
(closures) in particular are very consistent, unlike JavaScript or most other
languages.
Lua has three fundamental mechanisms—one for "each major aspect of
programming": tables, closures, and coroutines.
https://www.lua.org/doc/cacm2018.pdf
- Tables are the "object" or container datastructure: they represent both
lists and maps, you can extend them to represent your own datatypes and
change their behavior using |metatable|s (like Python's "datamodel").
- EVERY scope in Lua is a closure: a function is a closure, a module is
a closure, a `do` block (|lua-do|) is a closure--and they all work the same.
A Lua module is literally just a big closure discovered on the "path"
(where your modules are found: |package.cpath|).
- Stackful coroutines enable cooperative multithreading, generators, and
versatile control for both Lua and its host (Nvim).
*lua-error-handling*
Lua functions may throw |lua-errors| for exceptional (unexpected) failures,
which you can handle with |pcall()|.
*lua-result-or-message*
When failure is normal and expected, it's idiomatic to return `nil` which
signals to the caller that failure is not "exceptional" and must be handled.
This "result-or-message" pattern is expressed as the multi-value return type
`any|nil,nil|string`, or in LuaLS notation: >
---@return any|nil # result on success, nil on failure.
---@return nil|string # nil on success, error message on failure.
<
Examples of the "result-or-message" pattern:
- |vim.ui.open()|
- |io.open()|
- |luv-error-handling|
When a caller can't proceed on failure, it's idiomatic to `assert()` the
"result-or-message" result: >lua
local value = assert(fn())
Guidance: use the "result-or-message" pattern for...
- Functions where failure is expected, especially when communicating with the
external world. E.g. HTTP requests or LSP requests often fail because of
server problems, even if the caller did everything right.
- Functions that return a value, e.g. Foo:new().
- When there is a list of known error codes which can be returned as a third
value (like |luv-error-handling|).
<
*iterator*
An iterator is just a function that can be called repeatedly to get the "next"
value of a collection (or any other |iterable|). This interface is expected by
|for-in| loops, produced by |pairs()|, supported by |vim.iter|, etc.
https://www.lua.org/pil/7.1.html
*iterable*
An "iterable" is anything that |vim.iter()| can consume: tables, dicts, lists,
iterator functions, tables implementing the |__call()| metamethod, and
|vim.iter()| objects.
*list-iterator*
Iterators on |lua-list| tables have a "middle" and "end", whereas iterators in
general may be logically infinite. Therefore some |vim.iter| operations (e.g.
|Iter:rev()|) make sense only on list-like tables (which are finite by
definition).
*lua-function-call*
Lua functions can be called in multiple ways. Consider the function: >lua
local foo = function(a, b)
print("A: ", a)
print("B: ", b)
end
The first way to call this function is: >lua
foo(1, 2)
-- ==== Result ====
-- A: 1
-- B: 2
This way of calling a function is familiar from most scripting languages. In
Lua, any missing arguments are passed as `nil`, and extra parameters are
silently discarded. Example: >lua
foo(1)
-- ==== Result ====
-- A: 1
-- B: nil
<
*kwargs*
When calling a function, you can omit the parentheses if the function takes
exactly one string literal (`"foo"`) or table literal (`{1,2,3}`). The latter
is often used to mimic