anchor.diff:                       (gensym "CALL"))))
   
   ;; TODO: add timeout and keepalive parameters if it seems reasonable
  -(defun invoke-rpc-method (sock service method args &key notification)
  +(defun invoke-rpc-method (con service method args &key notification)
     (let ((call (make-call-obj service method args)))
       (declare (special *rpc-batch*))
       ;; if *rpc-batch* is bound, just add the call to it
  
positive.code:         (write-request (socket con) (list call)))
      (if notification
          (values)
          (make-result-future (rpc-call-id call) sock))))
  
  (defmacro with-batch-calls ((stream &optional (batch-req nil batch-supplied-p)) &body body)
    `(let ((*rpc-batch* ,(if batch-supplied-p batch-req '())))
  
negative.code:   (:documentation "Read and unmarshall an RPC response from CON.")
    (:method ((con rpc-connection))
      (unmarshall-response (recv-string (usocket:socket-stream (socket con))))))
  
  (defgeneric send-request (con request &key flush)
    (:documentation "Marshall and send REQUEST over CON. If FLUSH is T, flush the output buffer.")
    (:method ((con rpc-connection) request &key (flush t))
      (check-type request rpc-request)
      (let* ((encoded-request (with-output-to-string (json:*json-output*)
                               (marshall-request request)))
             (request-data (trivial-utf-8:string-to-utf-8-bytes
                            encoded-request))
             (stream (usocket:socket-stream (socket con))))
        (cl-netstring+:write-netstring-bytes stream
                                             request-data)
        (when flush
          (finish-output stream)))))
  
  (define-condition duplicate-result-id ()
    ((id)))
  
  