Why does "disj" from miniKanren work in Scheme but not in Racket? - scheme

I was working with the minikanren library for Racket, but wanted to use the "disj" and "conj" operators. I want to be able to more explicitly declare whether I am using disj or conj for the sake of legibility rather than have to parse through a conde expression, especially once the expressions become more complicated. I copied the source from "The Reasoned Schemer":
(define (append∞ s∞ t∞)
(cond
((null? s∞) t∞)
((pair? s∞)
(cons (car s∞)
(append∞ (cdr s∞) t∞)))
(else (λ ()
(append∞ t∞ (s∞))))))
(define (disj2 g1 g2)
(λ (s)
(append∞ (g1 s) (g2 s))))
(define-syntax disj
(syntax-rules ()
[(disj) '()]
[(disj g) g]
[(disj g0 g ...) (disj2 g0 (disj g ...))]))
This works properly with the first two cases
> (run* (x) (disj (== 'foo x)))
'(foo)
but only returns the first result when using more than one goal:
> (run* (x) (disj (== 'foo x) (== 'bar x) (== 'foobar x)))
'(foo)
Why is this?

Hrmph. I can't seem to reproduce that behavior.
When I clone the TRS/2e repo, add both
#lang racket
(provide (all-defined-out))
to the top of trs2-impl.scm, run that file, and then try your test program I see the expected result:
;
; Welcome to Racket v7.9.0.3 [cs].
;
trs2-impl.scm> (run* (x) (disj (== 'foo x) (== 'bar x) (== 'foobar x)))
'((foo) (bar) (foobar))
Do you see different behavior? If so, then we could look deeper. Do you know which version of Racket you're using? I don't think that will matter, but just in case.

Related

Threading functions

Is there a simpler way to achieve threading functionality in racket? I know about the threading library but it seems like such a basic functionality that I wonder if there is not some builtin way to do this.
(define (thread x . fns)
(foldl (lambda (f a) (f a))
thread
fns))
Also, can you express (lambda (f a) (f a)) in a simpler fashion?
There are lots of ways of doing this. A nice one is to use a macro, a simple (and perhaps not completely correct) version of which is:
(define-syntax (/> stx)
(syntax-case stx ()
[(_ x)
#'x]
[(_ x f)
#'(f x)]
[(/> x f fs ...)
#'(/> (f x) fs ...)]
[/>
(identifier? #'/>)
#'(λ (x . fns)
(for/fold ([r x]) ([f fns])
(f r)))]))
Now, for instance (/> x sin cos) is expanded to (cos (sin x)): there is no run-time overhead at all. The last clause means that (apply /> 1 (list sin cos)) will work.
I'm not sure the above macro is completely correct, particularly the last clause.

Local Procedure Bindings

I'm new to Scheme and Lisp in general, and upon learning I've stumbled upon a cryptic syntax used in local procedure binding:
(define mock
(lambda (s)
;; this is what I don't understand
(let splice ([l '()] [m (car s)] [r (cdr s)])
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))))
It took me a while to grasp that splice is a scoped procedure with 3 arities. Rewriting this in an ML-esque style it seems to produce similar output:
(define mock2
(lambda (s)
;; define `splice` first
(define splice
(lambda (la lb lc)
(append
(map (lambda (x) (cons lb x)) lc)
(if (null? lc) '()
(splice (cons lb la) (car lc) (cdr lc))))))
;; bind `splice` and its arguments together and call it with them
(let ([sp splice] [l '()] [m (car s)] [r (cdr s)])
(splice l m r))))
The second version is a bit longer and somewhat look more imperative, but defining splice as a normal procedure inside the scope before binding it in parallel with the arguments (or just chuck them in as-is) and calling it looks saner.
Question is are these two versions replaceable? If yes, could you help explain the first version's syntax of binding local variables (l, m, and r) within the splice binding form?
Calling splice is like re-entering a loop, which is what it is there for. A tail call is a goto anyway. It is often named loop, instead of thinking up some special name for it.
"looks saner" is debatable, and actually with Schemers you'll lose this one, because this is a very popular Scheme construct, called "named let". It is usually re-written with letrec btw, if/when one wants to rewrite it, to understand it better. Internal define can be used as well, but then, why not use (define (mock s) ... in the first place.
So, the usual way to re-write this
(define mock ; or: (define (mock s) ...
(lambda (s)
(let splice ([l '()] [m (car s)] [r (cdr s)])
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))))
is this:
(define mock
(lambda (s)
(letrec ([splice (lambda (l m r) ; or: (define (splice l m r) ...
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))])
(splice '() (car s) (cdr s)))))
and writing it in the named let way saves one from having it defined in one place and called in another, potentially far away. A call enters its body from the start anyway, and named let better reflects that.
This is pretty self-explanatory. The transformation from one form to the other is purely syntactical, and both can be used interchangeably.

Simplify conditional expression

Is it possible to rewrite the expression below so that (foo) only appears once? As it stands it's not terrible, but these repeated calls to (foo) make the code hard to read.
(cond
(a
(x)
(foo))
(b
(y)
(foo))
(else
(z)
(bar)))
I'm not sure this is technically "answering your question," but I would disagree that the repeated calls to foo make the code "hard to read". Indeed, I would claim that they make it easier to read. Specifically, in the refactored versions, you have conditional branches with trailing code (the call to foo). This means that as a reader, I have to keep in mind while I'm reading the conditional the fact that there's more that happens later. Even more importantly, looking at the foo in the refactored code, we can't say for sure what code executed before this one; the original one didn't have this problem.
When large blocks of code are repeated, it makes code hard to read and fragile. When a single call is repeated, as here, it generally makes the code easier to read, especially if the function has a meaningful name.
Here, let me reindent that for you:
(cond
[a (x)
(foo)]
[b (y)
(foo)]
[else (z)
(bar)])
If by (foo) you mean something complicated, I suggest:
(let ()
(define (f) (foo))
(cond
(a (x) (f))
(b (y) (f))
(else (z) (f))))
or
(let ((f (lambda () (foo))))
(cond
(a (x) (f))
(b (y) (f))
(else (z) (f))))
Not necessarily more readable :) but this will work.
(cond ((or a b) ((cond (a (x)) (b (y))) (foo)) (else (z) (bar)))
Yes,
(cond
((cond
(a
(x)
#t)
(b
(y)
#t)
(else
(z)
#f)) (foo))
(else (bar)))
This does not introduce additional testing, in case a or b are complex expressions.
Yet another option:
(let ((myfoo (lambda (x) (x) (foo))))
(cond (a (myfoo x))
(b (myfoo y))
(else (z) (bar))))

The code of the build-in Scheme procedure "pair?"

I started to study the Scheme and curious of how the built-in procedure "pair?" works. I mean the code obviously, since I couldn't find a way to see the code of the built-in procedures and don't know how to write it I'm here.
Had the same question with the "List?" procedure but managed to write it myself, but in case of "pair?" have no idea. Thx!
I think you're looking for the implementation of the 'pair?' primitive in Racket. If so: it's currently in list.c:
https://github.com/racket/racket/blob/master/racket/src/racket/src/list.c
Specifically, look at the definition of pair_p_prim.
Hope this helps!
EDIT: why isn't it written in Racket?
Answer: pair? is a primitive in Racket and Scheme. This means that in Racket, it's not implemented in Racket, it's implemented in the language that Racket is implemented in. For this part of the language, that's C. Keep in mind that this can change; if the Racket implementation gets updated to provide a lower-level set of primitives, then the pair? function might no longer be a primitive. Finally, it's worth noting that for some languages, implementors leverage the existence of an older compiler in order to provide a 'bootstrapping' implementation where the implementation language is the same as the language
being developed.
Hope this helps!
pair? can be implemented in Scheme; Scheme is Turing complete, my friend!
But instead of doing your hw for you and to get your head spinning I will encode pair? in the lambda calculus using Scheme; from here, follow the white rabbit!
cpair? = λm. m (λx. λy. tru) fls
In Scheme:
(define c-pair?
(lambda (m)
((m (lambda (x) (lambda (y) tru))) (lambda (x) fls))))
(define tru
(lambda (t) (lambda (f) t)))
(define fls
(lambda (t) (lambda (f) f)))
Procedures for testing:
(define kons
(lambda (h)
(lambda (t)
(lambda (c)
(lambda (n)
((c h) ((t c) n)))))))
(define c-equal?
(lambda (m)
(lambda (n)
((c-and (iszero ((m prd) n)))
(iszero ((n prd) m))))))
(define c-and
(lambda (b)
(lambda (c)
((b c) fls))))
(define iszero (lambda (m) ((m (lambda (x) fls)) tru)))
(define prd (lambda (m) (fst ((m ss) zz))))
(define fst (lambda (p) (p tru)))
;; church-boolean -> real boolean
(define real-bool (lambda (b) ((b true) false)))
(define nil (lambda (c) (lambda (n) n)))
;; representation of number 1
(define c1 (lambda (s) (lambda (z) (s z))))
Test:
;; some list - in Scheme this would be: (cons 1 '())
(define d ((kons c1) nil)
(real-bool ((c-equal? (c-pair? d)) tru)) ;; -> #t
(real-bool ((c-equal? (c-pair? c1)) fls)) ;; -> #f
In other words; you can definitely write pair? in Scheme even if pair? is a primitive.

Y combinator discussion in "The Little Schemer"

So, I've spent a lot of time reading and re-reading the ending of chapter 9 in The Little Schemer, where the applicative Y combinator is developed for the length function. I think my confusion boils down to a single statement that contrasts two versions of length (before the combinator is factored out):
A:
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0 )
(else (add1
((mk-length mk-length)
(cdr l))))))))
B:
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length))))
Page 170 (4th ed.) states that A
returns a function when we applied it to an argument
while B
does not return a function
thereby producing an infinite regress of self-applications. I'm stumped by this. If B is plagued by this problem, I don't see how A avoids it.
Great question. For the benefit of those without a functioning DrRacket installation (myself included) I'll try to answer it.
First, let's use some sane (short) variable names, easily trackable by a human eye/mind:
((lambda (h) ; A.
(h h)) ; apply h to h
(lambda (g)
(lambda (lst)
(if (null? lst) 0
(add1
((g g) (cdr lst)))))))
The first lambda term is what's known as little omega, or U combinator. When applied to something, it causes that term's self-application. Thus the above is equivalent to
(let ((h (lambda (g)
(lambda (lst)
(if (null? lst) 0
(add1 ((g g) (cdr lst))))))))
(h h))
When h is applied to h, new binding is formed:
(let ((h (lambda (g)
(lambda (lst)
(if (null? lst) 0
(add1 ((g g) (cdr lst))))))))
(let ((g h))
(lambda (lst)
(if (null? lst) 0
(add1 ((g g) (cdr lst)))))))
Now there's nothing to apply anymore, so the inner lambda form is returned — along with the hidden linkages to the environment frames (i.e. those let bindings) up above it.
This pairing of a lambda expression with its defining environment is known as closure. To the outside world it is just another function of one parameter, lst. No more reduction steps left to perform there at the moment.
Now, when that closure — our list-length function — will be called, execution will eventually get to the point of (g g) self-application, and the same reduction steps as outlined above will again be performed (recalculating the same closure). But not earlier.
Now, the authors of that book want to arrive at the Y combinator, so they apply some code transformations to the first expression, to somehow arrange for that self-application (g g) to be performed automatically — so we may write the recursive function application in the normal manner, (f x), instead of having to write it as ((g g) x) for all recursive calls:
((lambda (h) ; B.
(h h)) ; apply h to h
(lambda (g)
((lambda (f) ; 'f' to become bound to '(g g)',
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst)))))) ; here: (f x) instead of ((g g) x)!
(g g)))) ; (this is not quite right)
Now after few reduction steps we arrive at
(let ((h (lambda (g)
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(g g)))))
(let ((g h))
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(g g))))
which is equivalent to
(let ((h (lambda (g)
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(g g)))))
(let ((g h))
(let ((f (g g))) ; problem! (under applicative-order evaluation)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))))
And here comes trouble: the self-application of (g g) is performed too early, before that inner lambda can be even returned, as a closure, to the run-time system. We only want it to be reduced when the execution gets to that point inside the lambda expression, after the closure was called. To have it reduced before the closure is even created is ridiculous. A subtle error. :)
Of course, since g is bound to h, (g g) is reduced to (h h) and we're back again where we started, applying h to h. Looping.
Of course the authors are aware of this. They want us to understand it too.
So the culprit is simple — it is the applicative order of evaluation: evaluating the argument before the binding is formed of the function's formal parameter and its argument's value.
That code transformation wasn't quite right, then. It would've worked under normal order where arguments aren't evaluated in advance.
This is remedied easily enough by "eta-expansion", which delays the application until the actual call point: (lambda (x) ((g g) x)) actually says: "will call ((g g) x) when called upon with an argument of x".
And this is actually what that code transformation should have been in the first place:
((lambda (h) ; C.
(h h)) ; apply h to h
(lambda (g)
((lambda (f) ; 'f' to become bound to '(lambda (x) ((g g) x))',
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst)))))) ; here: (f x) instead of ((g g) x)
(lambda (x) ((g g) x)))))
Now that next reduction step can be performed:
(let ((h (lambda (g)
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(lambda (x) ((g g) x))))))
(let ((g h))
(let ((f (lambda (x) ((g g) x)))) ; here it's OK
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))))
and the closure (lambda (lst) ...) is formed and returned without a problem, and when (f (cdr lst)) is called (inside the closure) it is reduced to ((g g) (cdr lst)) just as we wanted it to be.
Lastly, we notice that (lambda (f) (lambda (lst ...)) expression in C. doesn't depend on any of the h and g. So we can take it out, make it an argument, and be left with ... the Y combinator:
( ( (lambda (rec) ; D.
( (lambda (h) (h h))
(lambda (g)
(rec (lambda (x) ((g g) x)))))) ; applicative-order Y combinator
(lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst)))))) )
(list 1 2 3) ) ; ==> 3
So now, calling Y on a function is equivalent to making a recursive definition out of it:
( y (lambda (f) (lambda (x) .... (f x) .... )) )
=== define f = (lambda (x) .... (f x) .... )
... but using letrec (or named let) is better — more efficient, defining the closure in self-referential environment frame. The whole Y thing is a theoretical exercise for the systems where that is not possible — i.e. where it is not possible to name things, to create bindings with names "pointing" to things, referring to things.
Incidentally, the ability to point to things is what distinguishes the higher primates from the rest of the animal kingdom ⁄ living creatures, or so I hear. :)
To see what happens, use the stepper in DrRacket.
The stepper allows you to see all intermediary steps (and to go back and forth).
Paste the following into DrRacket:
(((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0 )
(else (add1
((mk-length mk-length)
(cdr l))))))))
'(a b c))
Then choose the teaching language "Intermediate Student with lambda".
Then click the stepper button (the green triangle followed by a bar).
This is what the first step looks like:
Then make an example for the second function and see what goes wrong.

Resources