How do I get a function's name as a symbol?

Viewed 418

I am trying to define a function func->symbol that takes a function and returns its name as a symbol. For example:

(define (pythagoras a b)
  (sqrt (+ (* a a) (* b b))))

;; #1
(func->symbol pythagoras)  ; Returns: 'pythagoras

;; #2
(func->symbol (if #t pythagoras sqrt))  ; Returns: 'pythagoras

;; #3
(let ((f (if #t pythagoras sqrt)))
  (func->symbol f))  ; Returns: 'pythagoras

;; #4
(let ((f (if #t pythagoras sqrt)))
  (let ((g f))
    (func->symbol g)))  ; Returns: 'pythagoras

This is a follow-up question on How do I get a definition's name as a symbol? which only deals with case #1. For case #1, a simple macro def->symbol is sufficient:

(define-syntax def->symbol
  (syntax-rules ()
    ((_ def) 'def)))

However, this macro definition does not pass cases #2, #3, #4. Is it possible to define func->symbol, or is Scheme not expressive enough for this?

2 Answers

In Racket, in many cases, you can get a function's name using object-name. But it is probably a bad idea to rely on this result for anything other than debugging.

Perhaps it's worth an answer which shows why this is not possible in any language with first-class functions.

I'll define what I mean by a language having first-class functions (there are varying definitions).

  • Functions can be passed as arguments to other functions, and returned as values from them.
  • Functions can be stored in variables and other data structures.
  • There are anonymous functions, or function literals.

Scheme clearly has first-class functions in this sense. Now consider this code:

(define a #f)
(define b #f)

(let ((f (lambda (x)
           (+ x 1))))
  (set! a f)
  (set! b f))

Let's imagine there is a function-name function, which, given a function, returns its name. What should (function-name a) return?

Well, the answer is that there's simply no useful value it can return (in Racket, (object-name a) returns f, but that's clearly exposing implementation details which might be useful for debugging but would be very misleading as a return value for a function-name procedure.

This is why such a procedure can't exist in general in a language with first-class functions: the function which maps from names to values is many-to-one and thus has no inverse.


Here is an example of the sort of disgusting hack you could do to make this 'work' and also why it's horrible. The following is Racket-specific code:

(define-syntax define/naming
  ;; Define something in such a way that, if it's a procedure,
  ;; it gets the right name.  This is a horrid hack.
  (syntax-rules ()
    [(_ (p arg ...) form ...)
     (define (p arg ...) form ...)]
    [(_ name val)
     (define name (let ([p val])
                    (if (procedure? p)
                        (procedure-rename p 'name)
                        p)))]))

And now, given

(define/naming a
  (let ([c 0])
    (thunk
     (begin0
       c
       (set! c (+ c 1))))))

(define/naming b a)

Then:

> (object-name a)
'a
> (object-name b)
'b
> (eqv? a b)
#f
> (a)
0
> (b)
1
> (a)
2

So a and b have the 'right' names, but because of that they are necessarily not the same object, which I think is semantically wrong: if I see (define a b) then I want (eqv? a b) to be true, I think. But a and b do capture the same lexical state, so that works, at least.

Related