roam/20240401204408-returns.org

2.0 KiB

returns

A library for Python providing implementations of various Monads.

Benefits

Explicit errors

Composition

Asynchronous composition

Drawbacks

Learning curve

Formatting call chains in YAPF

Call chaining formats poorly in YAPF. Consider the following example which performs the following steps:

  1. Builds a request object
  2. Wraps the request object in a Future
  3. Binds the request to the HTTP send method
  4. Binds the result to a response handler

The code, formatted by YAPF, looks like this:

  return FutureResult.from_result(
      self.build_request(request)).bind_async(safe_send).bind_result(
          self.load_response)

The same code, formatted instead by the Black formatter, looks like this:

          return (
              FutureResult.from_result(self.build_request(request))
              .bind_async(safe_send)
              .bind_result(self.load_response)
          )

A workaround is to use the pointfree module from returns, though this comes with the additional overhead of understanding and using the point-free functions.

  return flow(
      FutureResult.from_result(self.build_request(request)),
      pointfree.bind_async(safe_send),
      pointfree.bind_result(self.load_response),
  )

Opinionated Future IO

Awaiting Future and FutureResult returns IO and IOResult containers. While this makes some sense, it's frustrating that the IO wrapping isn't opt-in. While Future and Result offer the compositional benefits Monads offer to Python code, IO as a tool to indicate impure functions/values strikes me as more of an imposition on development to be added by choice. Having to unwrap those values is tedious and presents an unpleasant hurdle to interacting with vanilla Python code.