Elisp: defmacro with &body and &rest behave differently? - elisp

I thought &body and &rest were supposed to behave the same, so this puzzled me:
ELISP> (defmacro mrest (&rest rest))
mrest
ELISP> (mrest)
nil
ELISP> (mrest 1)
nil
ELISP> (mrest 1 2)
nil
ELISP> (mrest 1 2 3)
nil
ELISP> (mrest 1 2 3 4)
nil
ELISP> (defmacro mbody (&body body))
mbody
ELISP> (mbody)
*** Eval error *** Wrong number of arguments: (lambda (&body body) nil), 0
ELISP> (mbody 1)
*** Eval error *** Wrong number of arguments: (lambda (&body body) nil), 1
ELISP> (mbody 1 2)
nil
ELISP> (mbody 1 2 3)
*** Eval error *** Wrong number of arguments: (lambda (&body body) nil), 3
ELISP> (mbody 1 2 3 4)
*** Eval error *** Wrong number of arguments: (lambda (&body body) nil), 4
Why does elisp insist on mbody having exactly two arguments here?

The Emacs Lisp defmacro doesn't support &body at all. Therefore in your example, &body is the name of one of two mandatory arguments.
You want the cl-lib variant, cl-defmacro.

I think you just defined two inputs for the macro, &body and body since they are both valid symbols names, so it's logic to ask for two arguments.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Symbol-Type.html

Related

Nil is not the empty list?

I am learning Scheme and am using an online interpreter from repl.it. I am having some trouble understanding why this returns #f:
(eq? '() 'nil) ; ==> #f
In LISP 1.5 nil, 'nil, '() and () evaluates to nil. The reader translated () and nil into the same singleton empty list and it is self evaluating so that the quote is optional. nil is also the only false value and all other values are truthy. The very first Scheme implementation was hosted by a commercial Lisp compatible with 1.5 and much of the host were used transparently in the first Scheme. Common Lisp is a descendant of 1.5 and still works like this, but Scheme has had changes from report to report and these are the rules from R5RS and later:
The empty list is '(). It's quoted and evaluates to (). It is not self evaluating so () is invalid Scheme code. 'nil evalutes to nil which is a symbol and not the empty list. In Scheme the only false value is #f and thus the empty list is truthy.
In code:
'() ; ==> ()
'(1 . (2 . ()) ; ==> (1 2)
() ; ==> ERROR: Illegal empty application (not allowed)
(eq? '() 'nil) ; ==> #f (empty list and nil are different values)
(if '() #t #f) ; ==> #t (empty list is truthy)

How to set default or optional parameters in scheme?

I'm trying to figure out how to how to set default or optional parameters in Scheme.
I've tried (define (func a #!optional b) (+ a b)) but I can't find of a way to check if b is a default parameter, because simply calling (func 1 2) will give the error:
Error: +: number required, but got #("halt") [func, +]
I've also tried (define (func a [b 0]) (+ a b)) but I get the following error:
Error: execute: unbound symbol: "b" [func]
If it helps I'm using BiwaScheme as used in repl.it
This works fine in Racket:
(define (func a (b 0)) ; same as [b 0]
(+ a b))
For example:
(func 4)
=> 4
(func 3 2)
=> 5
...But it's not standard syntax, it depends on the Scheme interpreter being used. There's syntax for handling a variable number of arguments, it can be used to handle optional arguments with default values, but it won't look as pretty:
(define (func a . b)
(+ a (if (null? b) 0 (car b))))
How does it work? b is a list of arguments. If it's empty use zero, otherwise use the value of the first element.
Check if your Scheme implementation supports SRFI 89: Optional positional and named parameters.
MIT/GNU Scheme doc for this purpose.
(define (f a #!optional b)
(+ a
(if (default-object? b)
0
b)))
; test
(f 1) ; 1
(f 1 2) ; 3

What does '(list 1 2) means in Scheme?

I'm studying SICP and at the beginning of section 2.2.2 it gives the following code: (cons '(list 1 2) (list 3 4))) and says it constructs a list like ((1 2) 3 4). But when I typed it into DrRacket(I'm using Racket here actually) it produces '((list 1 2) 3 4) and if I write (cons (list 1 2) (list 3 4)) then it'll be alright. I know in Scheme '(1 2) is equal to (list 1 2) but what does '(list 1 2) mean?
It should mean "a list consisting of the atom list, the atom 1, and the atom 2". Until Scheme evaluates the list (which the single quote prevents), it doesn't treat "list" differently from any other string.
Scheme has a convenient syntax for representing data literals: prefix any expression with ' (single quote) and the expression, rather than being evaluated, will be returned as data
For more informations:
http://courses.cs.washington.edu/courses/cse341/04wi/lectures/14-scheme-quote.html
Fix output style
First off, When you use the #!racket language in DrRacket, the default way of printing is not printing it's representation but an expression that evaluates to the same. You can turn it off from the menu language >> choose language. You select Show details and under Output style you select write
After pressing Run, when evaluating 'test you will get the output test.
Typo in expression
In section 2.2.2 there is an expression (cons (list 1 2) (list 3 4)). It is not the same as what you wrote in the question, (cons '(list 1 2) (list 3 4)). While an expression (list 1 2) applies the procedure list with values 1 and 2 and thus becomes (1 2), the expression '(list 1 2) just return the quoted data (list 1 2) unchanged.
Thus:
(cons (list 1 2) (list 3 4)) ; ==> ((1 2) 3 4)
(cons '(list 1 2) (list 3 4)) ; ==> ((list 1 2) 3 4)
'(cons '(list 1 2) (list 3 4)) ; ==> (cons '(list 1 2) (list 3 4))
The notation 'foo makes a symbol named foo.
The notation '(foo bar) makes a list with two symbols named foo and bar.
In the same way '(list foo bar) makes a list of three symbols. The symbol 'list happens to be called list.
Now (list 'foo 'bar) makes a list of two symbols called foo and bar.

Common Lisp - Giving &rest Lists as Arguments

As a result of musings around an exercism problem, I am trying to write a function that takes an input number and an arbitrary length list of divisors to test, along with the expected divisibility (i.e. remainder 0) as a boolean, returning true if all expectations are met (defaulting to true if unspecified).
example input:
(divisible-by 10 (5 t) (4 f) 2) => t
My reading has lead to this attempt at creating the input for the function:
(defun divisible-by (numerator &rest args (&key divisors (divisorp t)))
(loop...))
My simple test cases for such an input type error out in various ways, and my searching via Google and directly here on Stack Overflow have not proved fruitful, leading me to believe my understanding is insufficient to generate the right keywords.
Pointers on how to implement such a function, where my attempts fall down or why such a function cannot be implemented as I have outlined would be gratefully received.
You don't need anything beyond &rest:
(defun divisible-p (number &rest divisors)
"Check whether number is divisible by divisors."
(dolist (spec divisors t) ; For each 'divisor' in divisors
(etypecase spec
;; If divisor is a list, test if modulus of first value is 0
;; then compare to second (boolean)
(cons (unless (eq (zerop (mod number (first spec)))
(second spec))
(return nil)))
;; If divisor is an integer, return t if modulus == 0
(integer (unless (zerop (mod number spec))
(return nil))))))
(divisible-p 10 '(5 t) '(4 nil) 2)
==> T
(divisible-p 10 '(5 t) '(4 nil) 2 3)
==> NIL
Note that you need to quote list arguments, otherwise you will get an error that there is no function 5.
I am not sure what you were trying to accomplish by using &key, but they cannot be repeated in CL, i.e., if you write
(defun foo (&key a) (print a))
(foo :a 1 :a 2 :a 3)
only 3 will be printed, 1 & 2 will be ignored.
I don't see a productive way to mix &rest and &key for solving this problem. Here's an example using only &key:
(defun divisible-by (numerator &key divisors not-divisors)
(flet ((dividesp (denom) (= 0 (mod numerator denom))))
(and (every #'dividesp divisors)
(notany #'dividesp not-divisors))))
;; Call like:
(divisible-by 10 :divisors (list 2 5) :not-divisors (list 4 6))
=> t
Your lambda list has a syntax error, something close to what you wrote but valid would look and be called like this:
(defun divisible-by (numerator &rest args &key divisors (divisorp t))
(print args)
(print divisors)
(print divisorp))
;; Calling would look like this
(divisible-by 10 :divisors (list 11 2) :divisorp nil)
-> (:DIVISORS (11 2) :DIVISORP NIL)
-> (11 2)
-> NIL
Your desired input is not exactly possible for a function. Function calls don't alter the syntax of their arguments: (5 t) would be the function 5 called with the argument t, but 5 isn't a function so you get an error regardless of the lambda list.
Defun takes an ordinary lambda list. There is no destructuring in that.
&Rest takes exactly one symbol, which is bound to the rest of the arguments after all required and optional parameters are filled. If you want to destructure it, use destructuring-bind inside the function body.
Sometimes it may be worthwhile to use a macro, which takes a destructuring lambda list, to preprocess a function calling form.

How does `let` work in Scheme?

I use let to create a temporary variable, and then use this temporary variable in the next statement. However, DrScheme complained,
let: bad syntax (not an identifier and expression for a binding) in: temp
This is my code snippet:
(define (case-one-helper str)
(let (temp (substring str (+ 3 (string-contains str "my"))))
(substring temp (string-contains temp " "))))
I wonder if the value of variable created by let has to be known at compiled time?
Edit
I've just figured out, missing ().
Thanks,
While not exactly the problem you're experiencing, but an aside based on your questioning about the sequence of evaluating the arguments, let is also "syntactic sugar" for a lambda followed by it's arguments that are first evaluated and then passed to the lambda which is then evaluated.
For instance:
(let ((a (list 1 2 3))
(b (list 4 5 6)))
(cons a b))
is the same as:
((lambda (list-a list-b) (cons list-a list-b)) (list 1 2 3) (list 4 5 6))
So, if you're ever wondering about evaluation sequence, the arguments are evaluated fully before the body is evaluated (and one argument cannot refer to an argument preceding it ... use let* for something that requires bindings like that).
You need to put another set of parentheses around your let declarations:
(define (case-one-helper str)
(let ((temp (substring str (+ 3 (string-contains str "my")))))
(substring temp (string-contains temp " "))))

Resources