Can someone explain equality to me in scheme/racket? - scheme

So I stumbled across this today and it has me puzzled.
(define (x) '(1))
(eq? (x) (x)) ;=> #t
(eq? '(1) '(1)) ;=> #f
(define (y) (list 1))
(eq? (y) (y)) ;=> #f
(eq? (list 1) (list 1)) ;=> #f
Can anyone explain what's happening here ?

When compiled this program
(define (x) '(1))
(eq? (x) (x))
(eq? '(1) '(1))
is compiled into (something like):
(define datum1 '(1))
(define datum2 '(1))
(define datum3 '(1))
(define (x) datum1)
(eq? (x) (x))
(eq? datum2 datum3)
Therefore (x) will always return the object stored in datum1.
The expressions (eq? '(1) '(1)) on the other hand will
find out that datum2 and datum3 does not store the same object.
Note: There is a choice for the compiler writer. Many Scheme implementation will compile the above program to:
(define datum1 '(1))
(define (x) datum1)
(eq? (x) (x))
(eq? datum1 datum1)
and then the result will be true in both cases.
Note: The documentation of quote doesn't explicitly state whether multiple occurrences of '(1) in a program will produce the same value or not. Therefore this behavior might change in the future. [Although I believe the current behavior is a deliberate choice]

eq? checks if the objects are the same (think "if the pointer refers to the same address in memory").
In the first case you're working with literals created at compile time. Comparing (and modifying) literals is generally undefined behaviour. Here it looks like procedure x returns the same literal every time, but in the second expression it looks like the 2 literals are not the same. As I said, undefined behaviour.
In the second case you're not working with literals but list creates a new list at execution time. So each call to y or list creates a fresh list.

uselpa's answer is correct.† I wanted to expand on what a quoted datum is, a little further, though.
As you know, all Scheme programs are internally read in as a syntax tree. In Racket, in particular, you use the read-syntax procedure to do it:
> (define stx (with-input-from-string "(foo bar)" read-syntax))
> stx
#<syntax::1 (foo bar)>
You can convert a syntax tree to a datum using syntax->datum:
> (syntax->datum stx)
'(foo bar)
quote is a special form, and what it does is return the quoted portion of the syntax tree as a datum. This is why, for many Scheme implementations, your x procedure returns the same object each time: it's returning the same portion of the syntax tree as a datum. (This is an implementation detail, and Scheme implementations are not required to have this behaviour, but it helps explain why you see what you see.)
And as uselpa's answer says, list creates a fresh list each time, if the list is non-empty. That's why the result of two separate non-empty invocations of list will always be distinct when compared with eq?.
(In Scheme, the empty list is required to be represented as a singleton object. So (eq? '() '()) is guaranteed to be true, as is (eq? (list) '()), (eq? (cdr (list 'foo)) (list)), etc.)
† I would not use the phrasing "undefined behaviour" for comparing literals because that's easily confused with the C and C++ meaning of UB, which is nasal demons, and although the result of comparing literals may not be what you expect, it would not cause your program to crash, etc. Modifying literals is nasal demons, of course.

Related

Standard way to handle quoted symbol in lisp macros in Scheme

For some code I was working I've needed to handle 'x inside macro. What is standard way of handling those values?
I have code like this:
(define (quoted-symbol? x)
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
(define-macro (test x)
(if (quoted-symbol? x)
`(begin
(display ',(cadr x))
(newline))))
(test 'hello) ;; 'hello will be expanded into list (quote hello)
Is this how this should be handled, or is just in macro you don't use quoted symbols?
NOTE: I'm not asking about hygienic macros (I'm asking about real lisp macros), so please no answers with hygienic macros.
EDIT:
My macro works correctly in Guile and BiwaScheme and in my own scheme like lisp in JavaScript. Here is better example:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(define (example arg)
(list arg (test 'world)))
(example 'hello)
the question was not about display, but about (cadr x).
EDIT2: You've asked so here you go, my macro:
(define-macro (--> expr . code)
"Helper macro that simplify calling methods on objects. It work with chaining
usage: (--> ($ \"body\")
(css \"color\" \"red\")
(on \"click\" (lambda () (print \"click\"))))
(--> document (querySelectorAll \"div\"))
(--> (fetch \"https://jcubic.pl\") (text) (match /<title>([^<]+)<\/title>/) 1)
(--> document (querySelectorAll \".cmd-prompt\") 0 \"innerText\")"
(let ((obj (gensym)))
`(let* ((,obj ,(if (and (symbol? expr) (not (null? (match /\./ (symbol->string expr)))))
`(.. ,expr)
`,expr)))
,#(map (lambda (code)
(let ((name (gensym))
(value (gensym)))
`(let* ((,name ,(cond ((quoted-symbol? code) (symbol->string (cadr code)))
((pair? code) (symbol->string (car code)))
(true code)))
(,value (. ,obj ,name)))
,(if (and (pair? code) (not (quoted-symbol? code)))
`(set! ,obj (,value ,#(cdr code)))
`(set! ,obj ,value)))))
code)
,obj)))
;; ---------------------------------------------------------------------------------------
(define (quoted-symbol? x)
"(quoted-symbol? code)
Helper function that test if value is quoted symbol. To be used in macros
that pass literal code that is transformed by parser.
usage:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(list 'hello (test 'world))"
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
the macro is used in my scheme like lisp in JavaScript, like the doc string suggest:
(--> document (querySelectorAll ".class") 0 "innerText")
I want to support:
(--> document (querySelectorAll ".class") 0 'innerText)
The code can be tested online at: https://jcubic.github.io/lips/ (You need to copy/paste the code since current version allow only method calls).
To get expansion you can use
(pprint (macroexpand (--> document (querySelector "x"))))
if it don't work (don't expand) it mean that macro is broken somehow.
dot is build in function that get property of an object and .. macro:
(define-macro (.. expr)
"(.. foo.bar.baz)
Macro that gets value from nested object where argument is comma separated symbol"
(if (not (symbol? expr))
expr
(let ((parts (split "." (symbol->string expr))))
(if (single parts)
expr
`(. ,(string->symbol (car parts)) ,#(cdr parts))))))
that can be use to get nested property like (.. document.body.innerHTML)
Scheme doesn't have "real lisp macros". Some implementations has something similar, but the forms have different names and uses. They are not portable at all.
The standard way of handling 'x is to handle it like an expression that gets evaluated in the expansion. Eg.
(define var 'x)
(test 'x)
(test var)
The two test forms should amount to the same even though the macro test gets (quote x) in the first and the symbol var in the second. At the time of the expansion var does not exist since the implementation can expand all the macros before starting.
You implementation of test will not work. Eg. the display might be run one or twice and then each time you call a procedure that uses it it will gfail since the expansion is the undefined value and it might not be fit for evaluation. eg.
(define (example arg)
(list arg (test 'w)))
When this is defined you get 'w or (quote w) printed with a newline and then the procedure it tries to store is:
(define (example arg)
(list arg #<undefined>))
Note that what constitutes the undefined value is chosen by the implementaion, but I know for sure that in many implementaions you cannot evaluate #<undefined>.

Maximum recursion error [duplicate]

I'm reading The Little Schemer. And thanks to my broken English, I was confused by this paragraph:
(cond ... ) also has the property of not considering all of its
arguments. Because of this property, however, neither (and ... ) nor
(or ... ) can be defined as functions in terms of (cond ... ), though
both (and ... ) and (or ... ) can be expressed as abbreviations of
(cond ... )-expressions:
(and a b) = (cond (a b) (else #f)
and
(or a b) = (cond (a #t) (else (b))
If I understand it correctly, it says (and ...) and (or ...) can be replaced by a (cond ...) expression, but cannot be defined as a function that contains (cond ...). Why is it so? Does it have anything to do with the variant arguments? Thanks.
p.s. I did some searching but only found that (cond ...) ignores the expressions when one of its conditions evaluate to #f.
Imagine you wrote if as a function/procedure rather than a user defined macro/syntax:
;; makes if in terms of cond
(define (my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))
;; example that works
(define (atom? x)
(my-if (not (pair? x))
#t
#f))
;; example that won't work
;; peano arithemtic
(define (add a b)
(my-if (zero? a)
b
(add (- a 1) (+ b 1))))
The problem with my-if is that as a procedure every argument gets evaluated before the procedure body gets executed. thus in atom? the parts (not (pair? x)), #t and #f were evaluated before the body of my-if gets executed.
For the last example means (add (- a 1) (+ b 1)) gets evaluated regardless of what a is, even when a is zero, so the procedure will never end.
You can make your own if with syntax:
(define-syntax my-if
(syntax-rules ()
((my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))))
Now, how you read this is the first part is a template where the predicate consequent and alternative represent unevaluated expressions. It's replaced with the other just reusing the expressions so that:
(my-if (check-something) (display 10) (display 20))
would be replaced with this:
(cond ((check-something) (display 10))
(else (display 20)))
With the procedure version of my-if both 10 and 20 would have been printed. This is how and and or is implemented as well.
You cannot define cond or and or or or if as functions because functions evaluate all their arguments. (You could define some of them as macros).
Read also the famous SICP and Lisp In Small Pieces (original in French).

"cond","and" and "or" in Scheme

I'm reading The Little Schemer. And thanks to my broken English, I was confused by this paragraph:
(cond ... ) also has the property of not considering all of its
arguments. Because of this property, however, neither (and ... ) nor
(or ... ) can be defined as functions in terms of (cond ... ), though
both (and ... ) and (or ... ) can be expressed as abbreviations of
(cond ... )-expressions:
(and a b) = (cond (a b) (else #f)
and
(or a b) = (cond (a #t) (else (b))
If I understand it correctly, it says (and ...) and (or ...) can be replaced by a (cond ...) expression, but cannot be defined as a function that contains (cond ...). Why is it so? Does it have anything to do with the variant arguments? Thanks.
p.s. I did some searching but only found that (cond ...) ignores the expressions when one of its conditions evaluate to #f.
Imagine you wrote if as a function/procedure rather than a user defined macro/syntax:
;; makes if in terms of cond
(define (my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))
;; example that works
(define (atom? x)
(my-if (not (pair? x))
#t
#f))
;; example that won't work
;; peano arithemtic
(define (add a b)
(my-if (zero? a)
b
(add (- a 1) (+ b 1))))
The problem with my-if is that as a procedure every argument gets evaluated before the procedure body gets executed. thus in atom? the parts (not (pair? x)), #t and #f were evaluated before the body of my-if gets executed.
For the last example means (add (- a 1) (+ b 1)) gets evaluated regardless of what a is, even when a is zero, so the procedure will never end.
You can make your own if with syntax:
(define-syntax my-if
(syntax-rules ()
((my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))))
Now, how you read this is the first part is a template where the predicate consequent and alternative represent unevaluated expressions. It's replaced with the other just reusing the expressions so that:
(my-if (check-something) (display 10) (display 20))
would be replaced with this:
(cond ((check-something) (display 10))
(else (display 20)))
With the procedure version of my-if both 10 and 20 would have been printed. This is how and and or is implemented as well.
You cannot define cond or and or or or if as functions because functions evaluate all their arguments. (You could define some of them as macros).
Read also the famous SICP and Lisp In Small Pieces (original in French).

Function for detecting quotes in Scheme code

I am trying to write a function that can check whether or not some input is a quotation for a syntax checker.
I have the following code:
(define is-quote-expression?
(lambda (v)
(equal? (car v) 'quote)
(is-quotation? (cdr v)))))
(define is-quotation?
(lambda (v)
(or (number? v)
(boolean? v)
(char? v)
(string? v)
(symbol? v)
(null? v)
(and (pair? v)
(is-quotation? (car v))
(is-quotation? (cdr v)))))
When I try to evaluate, I get the following:
(is-quote-expression? '1)
#f
(is-quote-expression? (cons 'quote 1))
#t
I think my TA told me that the Scheme environment replaced all "'" with "'quote", however, this does not seem to be the case. We are running Petite Chez Scheme.
What am I not seeing?
Thanks in advance.
There are a couple of problems with your code, for starters the (lambda (pair? v) part in is-quote-expression? is almost certainly a mistake (you're defining a lambda with two parameters called pair? and v).
Also, I believe you intended to call is-quotation? from is-quote-expression? only if v isn't a pair, so it doesn't make sense to ask again if (pair? v) in is-quotation?. And who said that a pair is a quotation only if both its car and cdr are quotations?
Here, I believe this is what you intended:
(define is-quote-expression?
(lambda (v)
(if (pair? v)
(equal? (car v) 'quote)
(is-quotation? v))))
(define is-quotation?
(lambda (v)
(or (number? v)
(boolean? v)
(char? v)
(string? v)
(symbol? v)
(null? v))))
I agree with Óscar's post, though, is-quote-expression? accepts a list rather than a pair and returns whether it was a quotation.
(define is-quote-expression?
(lambda (v)
(and (proper-list-of-given-length? v 2)
(equal? (car v) 'quote)
(is-quotation? (cadr v)))))
Also, your original question shows some confusion as to what quote actually does. This is what really should happen:
> (is-quote-expression? '1)
#f
> (is-quote-expression? (cons 'quote 1))
#f
> (is-quote-expression? (quote 42))
#f
> (is-quote-expression? (quote (quote 42)))
#t
Note how the built-in quote procedure simply returns what it is passed. In the case of (quote 42) it simply returns 42. But (quote (quote 42)) returns (quote 42), which is what you wish to pass to is-quote-expression?.
The behavior of quote is specified in R6RS, appendix A.3. For '1, the relevant rule is 6sqv:
(store (sf1 ...) S1[ 'sqv1 ]) → (store (sf1 ...) S1[ sqv1 ])
Time to break this down.
The "S → T" notation defines one step in evaluating a term, where "S" evaluates to "T".
Since store and the sf1 non-terminals appear the same on both the left and right sides, you don't need to understand them to understand how '1 evaluates. If you need something, think of store as "storage environment" and the sfn as pairs of names and values; (store ((x 1) (y 2)) S) means the identifier x is associated with the value 1 and y with 2 when evaluating the term S.
If you're not familiar with the S[e] notation, it refers to a term with one "hole" (an term with a [] in it) filled with e. There are two related syntax elements: terms with holes (S[]) and terms with a hole filled by a value (S[e]). Holes are a little (but only a little) like unnamed variables. One important difference is that a term is allowed only one hole. Holes are perhaps best explained by example. All of the following are S[]:
(+ [] 1 2)
(list [] 'a "foo")
(cond ((= x 0) [])
((> x 0) 1)
(else -1))
Note that a hole can only appear where a sub-term is syntactically valid; [] 2) is not a term-with-hole. S[0] would be a term with 0 substituted into the hole:
(+ 0 1 2)
(list 0 'a "foo")
(cond ((= x 0) 0)
((> x 0) 1)
(else -1))
When a value is given for a hole, the term S[] is also called a "context". This comes from one of the primary uses for terms-with-holes: to match any term containing the given value. S[e] is any term that contains e as a valid sub-term, so S[] is the context that e appears in. In short, S1['sqv1] is a stand-in for any term that contains a quote.
(+ 'foo 1 2)
(list 'bar 'a "foo")
(cond ((= x 0) 'baz)
((> x 0) 1)
(else -1))
Note the second term provides two different contexts for quote-terms: (list [] 'a "foo"), (list 'bar [] "foo"). This suggests that you shouldn't think of holes too much as just being unnamed variables.
If you're wonder why context terms and holes are used, they're an alternative to recursive definitions. Without contexts, → would have to be defined recursively over the structure of terms (Scheme's grammar defines the structure). Substitution in lambda calculus is an example of structural recursion, as are any tree-processing functions you might define in Scheme (though Scheme syntax is quite different than the syntax used to define → and lambda calculus substitution).
(define (tree-depth tree)
(if (pair? tree)
(max (tree-depth (car tree))
(tree-depth (cdr tree)))
1
) )
Next, let's examine the meaning of sqv, which is short for "self-quoting values". This is a non-terminal from the Scheme grammar, given in appendix A.2.
sqv ::= n | #t | #f
n ::= [numbers]
sqv is simply a number or boolean.
All together, the 6sqv evaluation rule means that a quoted number or boolean evaluates to the number or boolean; the quote is simply discarded.
What this signifies for your homework is that you can't tell the difference between 1 and '1 with a normal function, since sub-terms are evaluated before the function is called. You'll need a macro.
Why go through all this just to say that "'1 evaluates to 1"? Firstly, answering "why" takes more than answering "what". Also, it will hopefully help you go beyond the question, learning a little bit how to read Scheme's formal semantics, give you a taste of computational theory concepts and lead to many more questions.

How can i overload a function at run time in Scheme?

rt.
I want to redefine a function at run time so that i can change the behavior of the system at run time.
thanks.
(define (foo x) ...stuff...)
(set! foo (lambda (x) ...different stuff...))
It might be advisable to use let to do this locally, this can also apply to keywords in this sense:
(let ((define +))
(define 2 3)) ; ===> 5
Or even redefine them to constants, remember, Scheme is a lisp-1:
(let ((define 2) (+ 4))
(- define +)) ; ===> -2
Or even:
(let ((quote /))
'3) ===> 1/3
Doing it only locally preserves the functional style.
Assuming you want to overload a function you defined earlier, simply define it again. This also works for redefining functions such as car and cdr, e.g. to make car into cdr:
(define (car x) (cdr x))
However, I think you won't be able to affect other already defined functions with such a redefinition, so a system function which uses car will still use the original system car and not yours:
(define (test x) (car x))
(define (car x) (cdr x))
(test '(1 2 3))
1
I guess the reason for this is that internally the symbols disappear once a function gets read or evaluated and the symbols are replaced by what they're bound to; in this case, the actual code of the function. So rebinding a symbol to a different function won't affect the rest of your already defined code. This is usually a good thing because it helps uphold referential transparency.
If you want to redefine scheme keywords such as lambda or cond, use let-syntax (see http://community.schemewiki.org/?scheme-faq-language)

Resources