unify async/sync interface when wrapping functions

Open
#436 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
25/100
Issue type
Feature
Clarity
Needs clarification
Activity status
Stale
Tech stack
lua
Domain
tooling

Research direction

Start with the plenary.async entry point and the luv file-system operations documentation linked in the issue. Compare the return conventions used by vim.loop and plenary.async. Done means a wrapped file-reading function can be called from both coroutine and non-coroutine contexts with consistent error handling.

Written by the indexing model from the issue text.

Description

Suppose I'm writing a function that reads the contents of a file. If called from an asynchronous context (in a coroutine), I want the code to be asynchronous, and likewise if called in a synchronous context (not in a coroutine).

-- This version only works in async mode (coroutine.running() ~= nil)
local function readfile(path)
  local uv = coroutine.running() and require("plenary.async").uv or vim.loop

  -- Functions return err, retval.
  local err, fd = uv.fs_open(path, "r", 438)
  assert(not err, err)
  local err, stat = uv.fs_fstat(fd)
  assert(not err, err)
  local err, data = uv.fs_read(fd, stat.size, 0)
  assert(not err, err)
  local err = uv.fs_close(fd)
  assert(not err, err)

  return data
end

Right now, the readfile function only works when calling in an async context. E.g.:

local a = require("plenary/async")

a.run(function()
  local data = readfile("myfile.txt")
  parse_my_data(data)
end)

It does not work in a synchronous context because vim.loop.fs_open and friends return a single return value (fd or error). I have a preference for the vanilla version because error-checking can be put on the same line:

-- This version only works in sync mode (coroutine.running() == nil)
local function readfile(path)
  local uv = coroutine.running() and require("plenary.async").uv or vim.loop

  -- Functions return "retval or err"
  local fd = assert(uv.fs_open(path, "r", 438))
  local stat = assert(uv.fs_fstat(fd))
  local data = assert(uv.fs_read(fd, stat.size, 0))
  assert(uv.fs_close(fd))

  return data
end

I'm sure there's a reasoning behind the current approach and why it's hard, but I'd like to ask whether it's possible to do so. Perhaps this won't be an issue anymore if https://github.com/luvit/luv/pull/618 ever becomes a thing and Neovim starts running things in a coroutine by default.

On closer inspection the dichotomy is likely due to how the callback/async version of the luv calls themselves pass err, args: https://github.com/luvit/luv/blob/master/docs.md#file-system-operations.

Dominant language
Lua
Stars
3.5k
Forks
340
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from nvim-lua/plenary.nvim

All issues in nvim-lua/plenary.nvim

Similar issues

More Lua issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.