docs: def gen(function, io_loop=None):
      """Allows using a generator to chain together reversible actions.
  
      This function is very similar to :py:func:`reversible.gen` except that it
      may be used with actions whose ``forwards`` and/or ``backwards`` methods
      are couroutines. Specifically, if either of those methods return futures
      the generated action will stop execution until the result of the future is
      available.
  
      .. code-block:: python
  
          @reversible.tornado.gen
          @tornado.gen.coroutine
          def save_comment(ctx, comment):
              ctx['comment_id'] = yield async_http_client.fetch(
                  # ...
              )
              raise tornado.gen.Return(ctx['comment_id'])
  
          @save_comment.backwards
          def delete_comment(ctx, comment):
              # returns a Future
              return async_http_client.fetch(...)
  
          @reversible.tornado.gen
          def post_comment(post, comment, client):
              try:
                  comment_id = yield save_comment(comment)
              except CommentStoreException:
                  # Exceptions thrown by actions may be caught by the
                  # action.
                  yield queue_save_comment_request(comment)
              else:
                  yield update_comment_count(post)
                  update_cache()
  
      :param function:
          The generator function. This generator must yield action objects. The
          ``forwards`` and/or ``backwards`` methods on the action may be
          asynchronous operations returning coroutines.
      :param io_loop:
          IOLoop used to execute asynchronous operations. Defaults to the
          current IOLoop if omitted.
      :returns:
          An action executable via :py:func:`reversible.tornado.execute` and
          yieldable in other instances of :py:func:`reversible.tornado.gen`.
      """
  
      @functools.wraps(function)  # TODO: use wrapt instead?
      def new_function(*args, **kwargs):
          try:
              value = function(*args, **kwargs)
          except _RETURNS as result:
              return SimpleAction(
                  lambda ctx: ctx.value,
                  lambda _: None,
                  result,
              )
          else:
              if isinstance(value, types.GeneratorType):
                  return _TornadoGeneratorAction(value, io_loop)
              else:
                  return SimpleAction(
                      lambda _: value,
                      lambda _: None,
                      None,
                  )
  
      return new_function