• how to write these two procedures?

    From Julieta Shem@21:1/5 to All on Sat Jan 13 19:46:09 2024
    I've got two procedures that are nearly identical. They're only
    different in the :data-argument in make-response. (I've simplified them
    a bit to make them short and easy to read, but they're very close to the originals.)

    --8<---------------cut here---------------start------------->8---
    (defun head-response (r g i)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (article-headers (parse-article a))))))))

    (defun body-response (r g i)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (article-body (parse-article a))))))))
    --8<---------------cut here---------------end--------------->8---

    Their differences are

    head-response: (article-headers (parse-article a))
    body-response: (article-body (parse-article a))

    I wouldn't think I can express these as a /thunk/ because they depend on
    the variable /a/ that is defined in a certain context in each procedure.

    Is this the case for a macro? What do you advise? Thank you!

    Keep in mind that I might write other procedures. For instance, here's
    another one that's already written:

    --8<---------------cut here---------------start------------->8---
    (defun article-response (r g i)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (encode-body a))))))) --8<---------------cut here---------------end--------------->8---

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alan Bawden@21:1/5 to All on Sat Jan 13 18:14:24 2024
    Julieta Shem <jshem@yaxenu.org> writes:

    I've got two procedures that are nearly identical. They're only
    different in the :data-argument in make-response.
    ...
    Is this the case for a macro? What do you advise? Thank you!

    A macro? Why? What's wrong with:

    (defun foobar (r g i get-data)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (funcall get-data (parse-article a))))))))

    - Alan

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Julieta Shem on Sat Jan 13 23:04:36 2024
    On 2024-01-13, Julieta Shem <jshem@yaxenu.org> wrote:
    I've got two procedures that are nearly identical. They're only
    different in the :data-argument in make-response. (I've simplified them
    a bit to make them short and easy to read, but they're very close to the originals.)

    --8<---------------cut here---------------start------------->8---
    (defun head-response (r g i)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (article-headers (parse-article a))))))))

    (defun body-response (r g i)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (article-body (parse-article a))))))))
    --8<---------------cut here---------------end--------------->8---

    Here is one (untested) possibility:

    (defmacro prepare-article-response (r g i avar expr)
    `(let ((,avar (handler-case (fetch-article ,g ,i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep ,avar 'response) ,avar)
    (t (prepend-response-with
    (format nil "~a ~a" ,i (extract-mid ,avar))
    (make-response :multi-line 'yes :code 200
    :request ,r :data ,expr))))))

    (defun head-response (r g i)
    (prepare-article-response r g i a (article-headers (parse-article a))))

    (defun body-response (r g i)
    (prepare-article-response r g i a (article-body (parse-article a))))


    The redundant calls to parse-article seem wasteful, but that's an organizational issue beyond the present scope.

    Caveats: the i argument is evaluated twice, and r g i are not
    evaluated in the order they appear in the macro argument list.

    If this is only as a jig for writing functions like the above, where
    those expressions are symbols referring to arguments, that's not a
    problem.

    We can write the macro by passing a lambda into a function:

    (defun prepare-article-response-impl (r g i fun)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (funcall fun a)))))))

    (defmacro prepare-article-response (r g i avar expr)
    ^(prepare-article-response-impl ,r ,g ,i (lambda (,avar) ,expr)))

    defuns are as before. The order of evaluation issues disappear,
    sine we are no inseting the argument expressions r g i into
    a function argument list.


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Julieta Shem on Sat Jan 13 23:08:05 2024
    On Sat, 13 Jan 2024 19:46:09 -0300, Julieta Shem wrote:

    Their differences are

    head-response: (article-headers (parse-article a))
    body-response: (article-body (parse-article a))

    How about this (sorry, more of a Python programmer than a Lisp programmer,
    but hopefully you get the idea):

    (defun make-response (article-part)
    (lambda (r g i)
    (let
    (
    (a
    (handler-case
    (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c))
    )
    )
    )
    (cond
    ((typep a 'response)
    a
    )
    (t
    (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response
    :multi-line 'yes
    :code 200
    :request r
    :data (article-part (parse-article a))
    )
    )
    )
    ) ; cond
    ) ; let
    ) ; lambda
    ) ; make-response

    (fset head-response (make-response (symbol-function article-headers)))
    (fset body-response (make-response (symbol-function article-part)))

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Alan Bawden on Sat Jan 13 20:32:29 2024
    Alan Bawden <alan@csail.mit.edu> writes:

    Julieta Shem <jshem@yaxenu.org> writes:

    I've got two procedures that are nearly identical. They're only
    different in the :data-argument in make-response.
    ...
    Is this the case for a macro? What do you advise? Thank you!

    A macro? Why? What's wrong with:

    (defun foobar (r g i get-data)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (funcall get-data (parse-article a))))))))

    That's pretty simple. Thanks. That's what I'm going to do here. I'll
    leave the macro for when it's really needed --- learning to distinguish
    when they're needed and when they're not.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alan Bawden@21:1/5 to All on Sat Jan 13 18:49:20 2024
    Julieta Shem <jshem@yaxenu.org> writes:

    That's pretty simple. Thanks. That's what I'm going to do here. I'll
    leave the macro for when it's really needed --- learning to distinguish
    when they're needed and when they're not.

    The #2 trap that novice Lisp programmers fall into is using macros when
    they don't need to. A novice that thinks they need to define a macro is probably wrong.

    The #1 trap is calling EVAL. A novice that thinks they need to call
    EVAL is always wrong.

    - Alan

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Alan Bawden on Sat Jan 13 21:02:53 2024
    Alan Bawden <alan@csail.mit.edu> writes:

    Julieta Shem <jshem@yaxenu.org> writes:

    That's pretty simple. Thanks. That's what I'm going to do here. I'll
    leave the macro for when it's really needed --- learning to distinguish
    when they're needed and when they're not.

    The #2 trap that novice Lisp programmers fall into is using macros when
    they don't need to. A novice that thinks they need to define a macro is probably wrong.

    The #1 trap is calling EVAL. A novice that thinks they need to call
    EVAL is always wrong.

    Thanks for the advice. I haven't called eval yet, thankfully. :-) But I
    think I can easily fall for #2. I'll post here all my macros to see
    what you guys think.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Kaz Kylheku on Sat Jan 13 20:31:27 2024
    Kaz Kylheku <433-929-6894@kylheku.com> writes:

    On 2024-01-13, Julieta Shem <jshem@yaxenu.org> wrote:
    I've got two procedures that are nearly identical. They're only
    different in the :data-argument in make-response. (I've simplified them
    a bit to make them short and easy to read, but they're very close to the
    originals.)

    --8<---------------cut here---------------start------------->8---
    (defun head-response (r g i)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (article-headers (parse-article a))))))))

    (defun body-response (r g i)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (article-body (parse-article a))))))))
    --8<---------------cut here---------------end--------------->8---

    Here is one (untested) possibility:

    (defmacro prepare-article-response (r g i avar expr)
    `(let ((,avar (handler-case (fetch-article ,g ,i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep ,avar 'response) ,avar)
    (t (prepend-response-with
    (format nil "~a ~a" ,i (extract-mid ,avar))
    (make-response :multi-line 'yes :code 200
    :request ,r :data ,expr))))))

    Nice. I hadn't thought of the /avar/ idea.

    (defun head-response (r g i)
    (prepare-article-response r g i a (article-headers (parse-article a))))

    (defun body-response (r g i)
    (prepare-article-response r g i a (article-body (parse-article a))))


    The redundant calls to parse-article seem wasteful, but that's an organizational issue beyond the present scope.

    That's interesting. How would you avoid that redundancy? Any small
    example would be nice.

    We can write the macro by passing a lambda into a function:

    (defun prepare-article-response-impl (r g i fun)
    (let ((a (handler-case (fetch-article g i)
    (sb-ext:file-does-not-exist (c)
    (error-response c)))))
    (cond ((typep a 'response) a)
    (t (prepend-response-with
    (format nil "~a ~a" i (extract-mid a))
    (make-response :multi-line 'yes :code 200
    :request r :data (funcall fun a)))))))

    (defmacro prepare-article-response (r g i avar expr)
    ^(prepare-article-response-impl ,r ,g ,i (lambda (,avar) ,expr)))

    That's pretty nice as well --- clears up a bunch of lambda expressions.

    Thank you!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Sun Jan 14 00:14:45 2024
    On Sat, 13 Jan 2024 23:08:05 -0000 (UTC), I wrote:

    :data (article-part (parse-article a))

    That “(article-part ...” should be “(funcall article-part ...”, since the
    function is being passed as the value of a variable.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)