How do I provide a list of functions in Racket?

Viewed 151

If I have a function that I want to be available outside of the current module, I can do the following...

(provide my-function)

Can I do this for a list of functions?

I tried the following...

(define f1 ...) ; body omitted for clarity
(define f2 ...) ; ditto

(define my-funs '(f1 f2))

(provide my-funs)

...but this gave "Unbound identifier in: f1" when I tried it.

Can I do this? Thanks

Update: Just to clarify what I'm trying to do here, I am working my way through Beautiful Racket, and am doing the first tutorial. At the stage where he defines the expander, he adds a handle function to handle the operators...

(define (handle [arg #f])
  (cond
    [(number? arg) (push-stack! arg)]
    [(or (equal? * arg) (equal? + arg))
     (define op-result (arg (pop-stack!) (pop-stack!)))
     (push-stack! op-result)]))

But then, in order to make this work, he provides both + and *...

(provide + *)

This means that these two operators are hard-coded twice. When adding support for other operators, you'd need to modify the handle function and the provide call. I am trying to work out if we can define a list of operators, and use that in both, so you'd only need to make one modification to support new operators.

1 Answers

No, you can't do this.

You can export a list of functions by using filtered-out and begin-for-syntax (as seen below), but this prevents you from using the list within your code.

Exporting a list

#lang racket

(module fns racket
  (require racket/provide)
  (define (f1 a) (+ a 1))
  (define (f2 a) (+ a 2))

  (begin-for-syntax
    (define my-funs '(f1 f2)))

  (provide
    (filtered-out
      (λ (name) (and (member (string->symbol name) my-funs) name))
      (all-defined-out))))

(require 'fns)
(display (f1 2))

How this works

provide can take any number of provide-spec forms and specifying multiple provide-specs is equivalent to writing multiple provide forms. One of the available provide-spec forms is all-defined-out, which will export all defined symbols in the module (or file if a module isn't explicitly specified).

By requiring racket/provide, we get access to helper functions that can transform and operate on provide-spec forms; filtered-out in particular allows us to run arbitrary code over a provide-spec and returns a valid provide-spec. (The required proc-expr is a function that takes a string (the string value of the exported identifiers) and returns a string or a falsy value. That's why when using member, we wrap it in an and and return the raw name itself. This could also be accomplished with findf: (λ (name) (findf (λ (n) (equal? (string->symbol name) n)) my-funs)))

However, this isn't quite enough, as provide is executed at "compile time", meaning that our list my-funs isn't available yet. To handle that, we need to wrap that definition in begin-for-syntax, which makes the binding available at "compile time" as well. But, by moving my-funs to "compile time", you lose the ability to use my-funs in non-"compile time" code. This means, for instance, you couldn't say (cond ... [(member arg my-funs) ...]):

(define (handle [arg #f])
  (cond
    [(number? arg) (push-stack! arg)]
    [(member arg my-funs)
              ;; ^--- Error here with "my-funs: unbound identifier"
     (define op-result (arg (pop-stack!) (pop-stack!)))
     (push-stack! op-result)]))
Related