Imagi007 · 2022-09-10 06:37
I was trying to do the following in elisp. The code actually works in racket, but I thought elisp would be similar. But seems like the lambda function is not allowed there, telling me f is void function when I try to run something like (triple 10).
(defun triple (x)
(letrec ((y (+ x 2))
(f (lambda (z) (+ z y w x)))
(w (+ x 7)))
(f -9)))
What would be the proper way to write such function in elisp?
lebensterben · 2022-09-10 06:53
cl-flet
Aminumbra · 2022-09-10 07:04
Racket is what is (usually) called a Lisp-1, and Emacs Lisp a Lisp-2. This means that in Racket, variable and functions share one, single namespace, while in Emacs Lisp they are disjoint. Conceptually, you can see each symbol as being (potentially) "bound" to two things, one in the "value slot", and one in the "function" slot. When you want to evaluate an expression such as `(f -9)`, you look for the function contained in the *function* slot of the symbol `f` (there is none, here).
Of course, as functions are first class objects, they can also be stored in the *value* slot of a symbol. But then, you need to be explicit to "use" them: to apply the function stored in the value slot of some symbol `f` with `n` arguments, you use `(funcall f arg1 ... argn)`; if you are given a list of arguments, you can use `(apply f list-of-args)`.
This means that the code `(let ((list 42)) (list list))` returns the list `(42)`: the symbol `list` is used both as a value and as a function.
Also try to look as what the `function` operator does, as well as the associated reader macro `#'`.
Disclaimer: all of this is not very formal. In particular, I should have explained what "store" means, the difference between lexical and dynamic scope (which is a prevalent "issue" in Emacs Lisp, everything being dynamic by default)
Imagi007 · 2022-09-10 07:09
But cl-flet seems to require all its arguments to be function definitions, but in my case the local bindings are just variables (one of which happens to be a lambda, thus turns out to be a function effectively).
I tried with cl-flet (replacing the letrec) but I am still getting the void function f error as before.
Isn't there any letrec equivalent of racket in elisp? Are the let and let\* functions having different behavior in elisp?
franburstall · 2022-09-10 07:19
So this does the job:
(defun triple (x)
(let* ((y (+ x 2))
(f (lambda (z) (+ z y w x)))
(w (+ x 7)))
(funcall f -9)))
Imagi007 · 2022-09-10 07:32
Thanks. After Aminumbra's explanation, the funcall thing makes sense.
Your solution works (at least on my system) if I move the w binding before the f binding since this is let\* (assuming it has the same semantics as in racket).
Imagi007 · 2022-09-10 07:34
Thanks for reminding me of Lisp-2. Now the use of funcall for f makes sense to me.
I know the dynamic scoping of elisp is going to cause problems for me (since I am so used to lexical scoping). But I read somewhere that elisp now does support some sort of lexical scoping using some syntax.
franburstall · 2022-09-10 07:34
It works for me as is but I would move the w binding too if I wasn't cut and pasting...
lebensterben · 2022-09-10 07:38
that's not true
(cl-flet* (((symbol-value foo) (..))
((symnol-function bar) (lambda ...)))
...)
let you bind both functions and values to symbols
usaoc · 2022-09-10 08:01
[Elisp does have a `letrec`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Local-Variables.html), but it isn’t as efficient (lacking the fancy [`fix`ing `letrec` optimization](https://legacy.cs.indiana.edu/~dyb/pubs/letrec-reloaded.pdf) present in modern Scheme implementations) and convenient (due to the [function call semantics](https://www.gnu.org/software/emacs/manual/html_node/elisp/Calling-Functions.html)) to use, so people don’t tend to use it. If you need mutually recursive [*function* bindings](https://www.gnu.org/software/emacs/manual/html_node/cl/Function-Bindings.html), use `cl-labels`, otherwise `cl-flet(*)` is more suitable. In this case you don’t really need mutually recursive bindings anyway—just move `y` and `w` to an outer `let` and bind `f` through `cl-flet` inside.
usaoc · 2022-09-10 08:04
You’re confusing the [modify macro](https://www.gnu.org/software/emacs/manual/html_node/cl/Modify-Macros.html) `cl-letf` with the [function binding construct](https://www.gnu.org/software/emacs/manual/html_node/cl/Function-Bindings.html) `cl-flet(*)`. Also note that this won’t work properly under lexical binding, since value and function cells of symbols are only used for *dynamic bindings*, i.e. special variables and global functions.
usaoc · 2022-09-10 08:10
All new Elisp code should enable [lexical *binding*](https://www.gnu.org/software/emacs/manual/html_node/elisp/Variable-Scoping.html) (not “scoping!” See [the discussion of scope and extent in CLtL](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node43.html)), so the discussion of value and function cells of symbols isn’t particularly relevant to local bindings.
lebensterben · 2022-09-10 08:18
you're right. what I meant was cl-letf.
Imagi007 · 2022-09-10 09:30
Thanks for the links. Cleared up a few confusions.