pablogav
pablogav

Reputation: 3

racket - how to use the elements of a list as a procedure (function)

I have a list that contains the "names" of some functions (for example '(+ - *))

I want to use that list to call the functions

(define list_of_names_of_functions '(+ - *))

(define sum (car list_of_names_of_functions))

(sum 2 3)

However, sum is a list, so can't be used as a procedure.

How should i do it?

Upvotes: 0

Views: 1157

Answers (4)

user5920214
user5920214

Reputation:

As I mentioned in a comment, using eval is not the right answer to this: it 'works' but it opens the way to horrors.

If you have a symbol like + and you want to get its dynamic value then the Racket approach to doing this is to use namespaces. A namespace is really a machine which turns symbols into values in the same way that eval does, but that's all it does, unlike eval. Unfortunately I don't understand namespaces very well, but this will work:

(define-namespace-anchor nsa)
(define ns (namespace-anchor->namespace nsa))

(define (module-symbol-value s (in ns))
  (namespace-variable-value s #t #f in))

Then

(define function-names '(+ - / *))
(define sum (module-symbol-value (first function-names)))

And now

> (sum 1 2 3)
6
> (eq? sum +)
#t
>

The problem with this approach is that it's leaky: if you have something like this:

(module msv racket
  (provide module-symbol-value)

  (define-namespace-anchor nsa)
  (define ns (namespace-anchor->namespace nsa))

  (define secret "secret")
  
  (define (module-symbol-value s (in ns))
    (namespace-variable-value s #t #f in)))

(require 'msv)

(define oops (module-symbol-value 'secret))

Now oops is secret. A way around this is to use make-base-namespace to get a namespace which is equivalent to racket/base.

Upvotes: 0

Will Ness
Will Ness

Reputation: 71065

The simple and immediate fix is to use (define list_of_functions (list+ - *)), then with (define sum (car list_of_functions)) it will work.

(edit:) This happens because list is a function which evaluates its argument(s) and returns the results in a list. Whereas quote (or ' in front of an expression) prevents its argument from being evaluated.

'(A B ...) is equivalent to (quote (A B ...)); that (A B ...) thing is a list of symbols, and quote preserves it as such.

It is a list of symbols because this is how Lisp code is read, either from a source file or from the REPL, i.e. at the interpreter's prompt. Other languages have their code as strings in a source code file, and compiler is some external magic which lives and works completely outside of the language. In Lisp-like languages, code is data. Code is read from textual source code file as (i.e. strings are turned into) nested lists of symbols, and explicit eval, as well as implicit evaluation, is deep inside the language itself.

Thus what you had is equivalent to the list

(define list_of_names (list '+ '- '*))

which contains unevaluated symbols, as opposed to the list

(define list_of_functions (list + - *))

which contains their values, which happen to be the usual built-in functions denoted by those "names".

And we can call a function, but we can't call a symbol (not in Scheme anyway).

Upvotes: 1

Sylwester
Sylwester

Reputation: 48745

The variable list_of_names_of_functions has symbol representation and not the actual function. you should perhaps make an alist with name and function like this:

(define ops `((+ . ,+) (- . ,-) (* . ,*)))

If you evaluate ops you'll see something like:

((+ . #<procedure:+>) 
 (- . #<procedure:->) 
 (* . #<procedure:*>))

There is no standard on how a function/procedure object should be represented, but it is expected that that value can be applied as any other function:

((cdar ops) 1 2) ; ==> 3

Thus you can search for + using:

(assq '+ ops)
; ==> (+ . #<procedure:+>)
(assq '/ ops)
; ==> #f

Thus if the result is truthy the cdr will contain a procedure:

(apply (cdr (assq '+ ops)) '(1 2)) ; ==> 3

If you are making a special kid of eval then you would consider the list an environment and it can easily be added to such that you can add /. You can also supply anything callable so an op doesn't need to exist in the host system. eg. `(square . ,(lambda (v) (* v v)) works as well.

Consider using the naming convention of Scheme (and Lisp). Eg. instead of list_of_names_of_functions it should be named list-of-names-of-functions

Upvotes: 1

pablogav
pablogav

Reputation: 3

I have just found the answer, sorry.

Solution will be to use the "eval" function

(define list_of_names_of_functions '(+ - *))

(define sum (car list_of_names_of_functions))

((eval sum) 2 3)

Upvotes: 0

Related Questions