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

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.

Related

Transform coroutine in continuation passing style

I have this coroutine example
(define p1
(lambda (continuation)
(display "1")
(newline)
(p1 (call/cc continuation))))
(define p2
(lambda (continuation)
(display "2")
(newline)
(p2 (call/cc continuation))))
(p1 p2)
And I would like to change it in CPS so that I can use the CPS call/cc :
(define (call/cc-cps f continuation)
(define (exit value actual-continuation)
(continuation value))
(f exit continuation))
I know that to transform a function in CPS I need to add a continuation to the fonction but I'm quite confused and I don't really know how to do it.
I think that it would look like that :
(define p1
(lambda (continuation)
(display "2")
(newline)
(call/cc-cps
(lambda (continuation actual-continuation)
continuation) ;; or actual-continuation ?
(lambda (value)
(p1 value)))))
(p1 p2)
But It's probably wrong. Can someone help me understand how to do it correctly ?
Thank you
If you do something in CPS only one calculation is done in tail position in every function. It's kind of the way an interpreter evaluates the code:
(define (hyp a b)
(sqrt (+ (square a) (square b))))
The whole point is that CPS does the calculation in the needed order and never do more than one thing at a time. This in CPS becomes:
(define (hyp& a b cont)
(define (cont-square-a sqa)
(define (cont-square-b sqb)
(define (cont-sum sum)
(sqrt& sum cont))
(+& sqa sqb cont-sum))
(square& b cont-square-b))
(square& a cont-square-a))
Or if you prefer anonymous continuations:
(define (hyp& a b cont)
(square& a
(lambda (sqa)
(square& b
(lambda (sqb)
(+& sqa
sqb
(lambda (sum)
(sqrt& sum cont))))))))
All the &-functions are just CPS-versions of the actual function, thus they have a continuation argument in addition to the original function. p1 would look like this:
(define (p1& continuation& cont)
(define (cont-display-2 undefined-value-1)
(define (cont-newline undefined-value-2)
(define (cont-call-cc-continuation& continuation-value)
(p1& continuation-value cont))
(call/cc& continuation& cont-call-cc-continuation&))
(newline& cont-newline))
(display& "2" cont-display-2))
You might be interested in the articles of Matt Might. Take a loot at everything around continuations and compilations as they are related. I also recommend watching the SICP videos and perhaps try solving the exercises in the SICP book. By the time you are making interpreters and compilers it should be a walk in the park making generators.

Does call/cc in Scheme the same thing with yield in Python and JavaScript?

Does call/cc in Scheme the same thing with yield in Python and JavaScript?
I am not clear about generators. In my opinion, yield gives a language the ability to generate iterators without pain. But I am not sure whether I am right.
Does call/cc in Scheme has something related to yield in other languages? If so, are they the same thing, or what is the difference?
Thanks!
call/cc is a much more general language feature than generators. Thus you can make generators with call/cc, but you cannot make call/cc with generators.
If you have a program that computes values and use those values in other places its basically a step machine.. One might think of it as a program that have one function for each step and a continuation for the rest of the steps. Thus:
(+ (* 3 4) (* 5 6))
Can be interpreted as:
((lambda (k)
(k* 3 4 (lambda (v34)
(k* 5 6 (lambda (v56)
(k+ v34 v56 k)))))
halt)
The k-prefix just indicate that it's a CPS-version of the primitives. Thus they call the last argument as a function with the result. Notice also that the order of evaluation, which is not defined in Scheme, is in fact chosen in this rewrite. In this beautiful language call/cc is just this:
(define (kcall/cc kfn k)
(kfn (lambda (value ignored-continuation)
(k value))
k))
So when you do:
(+ (* 3 4) (call/cc (lambda (exit) (* 5 (exit 6)))))
; ==> 18
Under the hood this happens:
((lambda (k)
(k* 3 4 (lambda (v34)
(kcall/cc (lambda (exit k)
(exit 6 (lambda (v6)
(k* 5 v6 k)))
k))))
halt)
By using the substitution we can prove that this actually does exactly as intended. Since the exit function is invoked the original continuation is never called and thus the computation cancelled. In contrast to call/cc giving us this continuation that doesn't seem obvious it's no magic in CPS. Thus much of the magic of call/cc is in the compiler stage.
(define (make-generator procedure)
(define last-return values)
(define last-value #f)
(define (last-continuation _)
(let ((result (procedure yield)))
(last-return result)))
(define (yield value)
(call/cc (lambda (continuation)
(set! last-continuation continuation)
(set! last-value value)
(last-return value))))
(lambda args
(call/cc (lambda (return)
(set! last-return return)
(if (null? args)
(last-continuation last-value)
(apply last-continuation args))))))
(define test
(make-generator
(lambda (collect)
(collect 1)
(collect 5)
(collect 10)
#f)))
(test) ; ==> 1
(test) ; ==> 5
(test) ; ==> 10
(test) ; ==> #f (procedure finished)
One might make a macro to make the syntax more similar, but it's just sugar on top of this.
For more examples I love Matt Mights page with lots of examples on how to use continuations.
Here's code to define a generator:
(define-syntax define-generator
(lambda (x)
(syntax-case x (lambda)
((stx name (lambda formals e0 e1 ...))
(with-syntax ((yield (datum->syntax (syntax stx) 'yield)))
(syntax (define name
(lambda formals
(let ((resume #f) (return #f))
(define yield
(lambda args
(call-with-current-continuation
(lambda (cont)
(set! resume cont)
(apply return args)))))
(lambda ()
(call-with-current-continuation
(lambda (cont)
(set! return cont)
(cond (resume (resume))
(else (let () e0 e1 ...)
(error 'name "unexpected return"))))))))))))
((stx (name . formals) e0 e1 ...)
(syntax (stx name (lambda formals e0 e1 ...)))))))
There are examples of the use of generators at my blog. Generators use call-with-current-continuation, in a manner similar to yield in Python, but are much more general.
You can implement generators with call/cc. Here is an example:
coroutines.ss
They work in a similar way to python and ECMAScript generators.

Why Scheme requires apply in Y-combinator implementation, but Racket doesn't?

Here is the Y-combinator in Racket:
#lang lazy
(define Y (λ(f)((λ(x)(f (x x)))(λ(x)(f (x x))))))
(define Fact
(Y (λ(fact) (λ(n) (if (zero? n) 1 (* n (fact (- n 1))))))))
(define Fib
(Y (λ(fib) (λ(n) (if (<= n 1) n (+ (fib (- n 1)) (fib (- n 2))))))))
Here is the Y-combinator in Scheme:
(define Y
(lambda (f)
((lambda (x) (x x))
(lambda (g)
(f (lambda args (apply (g g) args)))))))
(define fac
(Y
(lambda (f)
(lambda (x)
(if (< x 2)
1
(* x (f (- x 1))))))))
(define fib
(Y
(lambda (f)
(lambda (x)
(if (< x 2)
x
(+ (f (- x 1)) (f (- x 2))))))))
(display (fac 6))
(newline)
(display (fib 6))
(newline)
My question is: Why does Scheme require the apply function but Racket does not?
Racket is very close to plain Scheme for most purposes, and for this example, they're the same. But the real difference between the two versions is the need for a delaying wrapper which is needed in a strict language (Scheme and Racket), but not in a lazy one (Lazy Racket, a different language).
That wrapper is put around the (x x) or (g g) -- what we know about this thing is that evaluating it will get you into an infinite loop, and we also know that it's going to be the resulting (recursive) function. Because it's a function, we can delay its evaluation with a lambda: instead of (x x) use (lambda (a) ((x x) a)). This works fine, but it has another assumption -- that the wrapped function takes a single argument. We could just as well wrap it with a function of two arguments: (lambda (a b) ((x x) a b)) but that won't work in other cases too. The solution is to use a rest argument (args) and use apply, therefore making the wrapper accept any number of arguments and pass them along to the recursive function. Strictly speaking, it's not required always, it's "only" required if you want to be able to produce recursive functions of any arity.
On the other hand, you have the Lazy Racket code, which is, as I said above, a different language -- one with call-by-need semantics. Since this language is lazy, there is no need to wrap the infinitely-looping (x x) expression, it's used as-is. And since no wrapper is required, there is no need to deal with the number of arguments, therefore no need for apply. In fact, the lazy version doesn't even need the assumption that you're generating a function value -- it can generate any value. For example, this:
(Y (lambda (ones) (cons 1 ones)))
works fine and returns an infinite list of 1s. To see this, try
(!! (take 20 (Y (lambda (ones) (cons 1 ones)))))
(Note that the !! is needed to "force" the resulting value recursively, since Lazy Racket doesn't evaluate recursively by default. Also, note the use of take -- without it, Racket will try to create that infinite list, which will not get anywhere.)
Scheme does not require apply function. you use apply to accept more than one argument.
in the factorial case, here is my implementation which does not require apply
;;2013/11/29
(define (Fact-maker f)
(lambda (n)
(cond ((= n 0) 1)
(else (* n (f (- n 1)))))))
(define (fib-maker f)
(lambda (n)
(cond ((or (= n 0) (= n 1)) 1)
(else
(+ (f (- n 1))
(f (- n 2)))))))
(define (Y F)
((lambda (procedure)
(F (lambda (x) ((procedure procedure) x))))
(lambda (procedure)
(F (lambda (x) ((procedure procedure) x))))))

"Object Oriented" Scheme

(define-record-type car-ivars
(fields efficiency (mutable gas-in-tank)))
(define-record-type car-methods
(fields drive! get-gas-in-tank refuel))
(define refuel (lambda (c g) ((car-methods-refuel c) g)))
(define get-gas-level
(lambda (c) ((car-methods-get-gas-in-tank c))))
(define drive!
(lambda (c distance) ((car-methods-drive! c) distance)))
(define make-car
(lambda (efficiency)
(let ([car1 (make-car-ivars efficiency 0)])
(let ([set-gas-level!
(lambda (gas) (car-ivars-gas-in-tank-set! car1 gas))]
[gas-level
(lambda () ((car-ivars-gas-in-tank car1)))])
(make-car-methods
;;drive!
(lambda (distance)
(set-gas-level!
(- (get-gas-level)
(/ efficiency distance)))
;;get-gas-level
(get-gas-levels)
;;refuel
(lambda (gas1)
(set-gas-level!
(+ (get-gas-level) gas1)))))))))
Ok, so I have this code. When i try to run my test case of (define hybrid (make-car 50))
I get an Exception: incorrect number of arguments to #procedure constructor
error. And I'm not quite sure where it is coming from.
Your definition of the drive part in make-car is missing a right parenthesis, which means that make-car-methods only gets one argument.
Re-indenting in Emacs made this pretty obvious:
(define make-car
(lambda (efficiency)
(let ([car1 (make-car-ivars efficiency 0)])
(let ([set-gas-level!
(lambda (gas) (car-ivars-gas-in-tank-set! car1 gas))]
[gas-level
(lambda () ((car-ivars-gas-in-tank car1)))])
(make-car-methods
;;drive!
(lambda (distance)
(set-gas-level!
(- (get-gas-level)
(/ efficiency distance)))
;;get-gas-level
(get-gas-levels)
;;refuel
(lambda (gas1)
(set-gas-level!
(+ (get-gas-level) gas1)))))))))

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