1

I'm writing a program to log to various places. In it I have some log functions:

(define (write-to-file destination content)
      (with-output-to-file destination
          (λ ()
           (displayln 
            content))
        #:mode 'text #:exists 'append))

(define (write-to-port destination content)
  (displayln content destination))

I want to use these functions at run-time. So I have made a list of lists to hold my configuration:

(define log-destinations
  '((write-to-file "my.log")
    (write-to-port (current-error-port))))

So I have a function that recursively consumes the list log-destinations:

(define (send-log-content content destinations)
  (unless (null? destinations)
    (let ([ destination (car destinations)])
      (#%app (car destination) (cadr destination) content)) 
    (send-log-content content (cdr destinations))))

However at runtime I'm getting:

application: not a procedure;
 expected a procedure that can be applied to arguments
  given: 'write-to-file
  arguments...:
   "my.log"
   "[info] web: 2021/03/21 16:26:35 +11:00 /\n"
  context...:

Which suggests I've not quoted the function name properly. So how do I quote the function name appropriately in log-destinations so it can be called correctly at runtime?

robertpostill
  • 3,820
  • 3
  • 29
  • 38

1 Answers1

3

With the list literal here:

(define log-destinations
  '((write-to-file "my.log")
    (write-to-port (current-error-port))))

write-to-file and write-to-port are symbols, and (current-error-port) is a quoted list. You need the sublists in log-destinations to contain the procedures write-to-file and write-to-port. Further, you need to actually complete the evaluation of (current-error-port) to get the error port, which is to say that you need to evaluate the arguments within your lists.

Quoting a list to create a list literal means that the arguments will not be evaluated when the list is created. But, you can use the list procedure to create lists while evaluating the arguments:

(define log-destinations
  (list (list write-to-file "my.log")
        (list write-to-port (current-error-port))))

Alternatively, you could use the backquote syntax, i.e., quasiquotation. Quasiquotation allows you to create a list template with some control over how the list members are evaluated. Use a backquote to quasiquote a list, then place a comma before arguments which should be evaluated:

(define log-destinations
  `((,write-to-file "my.log")
    (,write-to-port ,(current-error-port))))

It isn't clear to me why you are using #%app; my guess is that you were thinking of (current-error-port) as a list in the procedure application. With the above changes you should be able to change the procedure call to:

((car destination) (cadr destination) content)
ad absurdum
  • 19,498
  • 5
  • 37
  • 60