1
votes

So I'm new to Scheme. I'm trying to make a function that defines global functions using a specification of the form ((name: name) (args: args) (body: body)) so for example

    (fn-maker '((name: mult5) (x) (* x 5)))

would make it so globally I could call

    (mult5 3)

and get 15.

Here's what I have so far:

    (define (fn-maker fn-spec)
        (let* (spec (map cdr fn-spec))
              (name (caar spec))
              (args (cadr fn-spec))
              (body (caaddr (cdaddr fn-spec))))
        (lambda (args)
          body)))

The main thing I'm confused on at the moment is how to make lambda use those args. As it stands, lambda creates a new local variable named "args" instead of evaluating the list that is behind args. Is there a way around this? My current thought process is that I should be using some form of let across the list provided by args but I'm not sure what that would look like or even how to go about structuring it.

This is homework so I'm most definitely not looking for code (cheating and all that) but rather a point in the right direction and some critique. Thank you.

Update: To anyone who happens upon this in the future, it is possible to do this code very simply using some clever quoting. No macros needed. Also, as it turns out, eval in Pretty Big evaluates in the global by default.

2
Did the instructions provide any rules about what functions or special forms you are or are not allowed to use? And is fn-maker really supposed to be a function, as in, you could pass it to apply or map? (If the latter is true, I don't see how to do this without falling back on using eval.)pnkfelix
Also, there is some relevant discussion at this other question: stackoverflow.com/questions/1894610/…pnkfelix
For rules, the test program is going to make a call of the form (fn-maker fn-spec) followed by (whatever-the-name-was some-args). We're not allowed to use set! and friends or any explicit looping functions (recursion is ok).Jess The Witch

2 Answers

1
votes

There is a Scheme procedure called eval that can be used to evaluate arbitrary source code that you construct.

Its use is generally discouraged (just like in Javascript and Ruby), since many times that it is used, it is being used as an unsafe short-cut when a more robust alternative is available. (A simple example is when someone might use (eval name) to look up the value associated with a symbol in the global environment, where name is drawn from a predetermined set of symbols; in this case, it is often better style to construct a separate lookup table with all of the symbols of interest, instead of subverting the global environment for this purpose.

Anyway, in some Scheme systems, eval can be used to inject new definitions into the global environments. I had to add the qualifier "some", because in R5RS, the eval procedure takes both an expression and the environment to evaluate that expression in, and implementations are not required to provide the interactive global environment for this purpose. (I.e., interaction-environment is an optional procedure.)

Here is an illustration of injecting a global variable via eval when interaction-environment is provided:

(define (make-it-three name) (eval (list 'define name '3) (interaction-environment)))

;; At the REPL now

> (make-it-three 'x)

> x
3

> (map make-it-three '(a b c))
(#!unspecified #!unspecified #!unspecified)

> (+ a b c)
9

In Racket (and also Pretty Big, I think), they do not provide interaction-environment. But I think there is a different procedure given there that can be used to accomplish this goal; check the docs.

Anyway, this was just a way of trying to provide a small note on one strategy that may or may not lead you in the right direction. Emphasis on "may not."

0
votes

You need a way to "export" a function definition created inside a procedure, in such a way that the definition exists outside, on the global environment. Your implementation so far will simply return a lambda form. Hint: there's a way to do this (I remember seeing it in Racket), but it might be specific to the Scheme interpreter in use; and I'm not sure if it's available in the Pretty Big language.

As a side note - if the fn-spec parameter is going to receive a list with the name and the body of the new function, then make sure that only a single parameter is passed when calling fn-maker, something like this:

(fn-maker '((name: mult5) (x) (* x 5)))

Also, consider using a macro instead of a plain procedure...