title: Common Lisp macro let-curry - not working
body: <p>I found myself calling lots of methods whose first argument is a complex object from a given class.
  Whilst with-slots and with-accessors are useful, generic methods cannot be bound in this way. So I thought: if we could locally curry any functions, slots + accessors + generic functions + functions could all be addressed with the same construct.</p>
  
  <p>Example of code I want to clean up:</p>
  
  <pre><code>(defun clox-string (scanner)
    "Parse string into a token and add it to tokens"
    (loop while (and (char/= #\" (peek scanner))
                     (not (at-end-p scanner)))
          do
             (if (char= #\Newline (peek scanner)) (incf (line scanner))
                 (advance scanner)))
    (when (at-end-p scanner)
      (clox.error::clox-error (line scanner) "Unterminated string.")
      (return-from clox-string nil))
    (advance scanner) ;; consume closing "
    (add-token scanner 'STRING (subseq (source scanner)
                                       (1+ (start scanner))
                                       (1- (current scanner)))))
  </code></pre>
  
  <p>This would be cleaner (I'm imitating this in CL <a href="https://craftinginterpreters.com/scanning.html#reserved-words-and-identifiers" rel="nofollow noreferrer">https://craftinginterpreters.com/scanning.html#reserved-words-and-identifiers</a> but I often end up with more verbose and less readable code than in Java - specially when using this classes a lot).  As in CL methods don't belong to classes you end up declaring such arguments over and over. This would be a bit better:</p>
  
  <pre><code>(defun clox-string (scanner)
    "Parse string into a token and add it to tokens"
    (let-curry scanner (peek at-end-p line source start current advance add-token)
     (loop while (and (char/= #\" (peek))
                      (not (at-end-p)))
           do
              (if (char= #\Newline (peek)) (incf (line))
                  (advance)))
     (when (at-end-p)
       (clox.error::clox-error (line) "Unterminated string.")
       (return-from clox-string nil))
     (advance) ;; consume closing "
     (add-token 'STRING (subseq (source)
                                (1+ (start))
                                (1- (current)))))
  </code></pre>
  
  <p>sketch of macro (not working):</p>
  
  <pre><code>;; Clearly not as I don't understand macros very well :) non-working code:
  (defmacro let-curry (obj functions &amp;body body)
    "Locally curry all functions"
    (let ((fn (gensym)))
      `(flet (loop
               for ,fn in ,functions
               collect (list ,fn (&amp;rest args)
                             (funcall ,fn ,obj args))) 
         ,@body)))
  </code></pre>
  
  <p>EDIT (ADD): Notice that <code>scanner</code> is a class; start, source, line, etc., accessors to the slots with the same name; add-token a generic function of more than one argument, advance a generic method of one argument:</p>
  
  <pre><code>(defclass scanner ()
    ((source
      :initarg :source
      :accessor source)
     ...
     (...)))
  
  (defmethod advance ((scanner scanner)) ...)
  (defmethod add-token ((scanner scanner) token-type) ...)
  </code></pre>
  
  <hr>
  
  <p>Simpler Example with error:</p>
  
  <pre><code>;; With 
  (defun add (x y) (+ x y))
  
  (defun mul (x y) (* x y))
  
  ;; I want to have this:
  (let-curry 1000 (add mul)
    (print (add 3))
    (print (mul 3)))
  
  
  ;; expanding to:
  (flet ((add (y) (add 1000 y))
         (mul (y) (mul 1000 y)))
    (print (add 3))
    (print (mul 3)))
  
  ;; but instead I'm getting:
  Execution of a form compiled with errors.
  Form:
    (FLET (LOOP
         FOR
         #1=#:G777
         IN
         (ADD MUL
           )
         COLLECT
         (LIST #1#
           (&amp;REST ARGS)
           (FUNCALL #1# 1000 ARGS)))
    (PRINT (ADD 3))
    (PRINT (MUL 3)))
  Compile-time error:
    The FLET definition spec LOOP is malformed.
     [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
  </code></pre>
  
  <p>Thanks! The basic question is: is it possible to make such macro work?</p>