I have the following piece of code for the successor and predecessor of Church numerals:
Consider the following code:
(define zero (lambda () '() )) ; Initialize Church numeral zero as nil
(define (succ x) (lambda () x)) ; Wraps x with another function and returns it
(define (pred x) (x)) ; "Unwraps" one shell function of x and returns it
(define one (succ zero)) ; gives me the Church numeral one
(pred one)
Suppose I do the following changes in the pred function:
(define (pred x) x)
What is the difference between returning x and (x)? What exactly does returning (x) mean syntactically and logically?
The function
(define (pred x) x)
is the identity function. The function accepts a value and binds it to the argument x. It then evaluates the body x which gives the original value back.
The function
(define (pred x) (x))
takes one value as input and binds it the argument x.
It then evaluates the body (x). The expression (x) means call (the hopefully a function) x with no arguments.
(pred (lambda () 42)) will evaluate to 42.
So in the context of your encoding (lamdda () x) wraps a function layer a value and (x) removes the function layer.
Related
I've just read "A Tutorial Introduction to the Lambda Calculus1" by Raul Rojas. I put the lambda expressions into Scheme (TinyScheme) to try them out. Everything worked except the recursive function to calculate the sum of the Church numbers 0,1,...,N using the Y-combinator which runs out of memory. Bizarrely, the Y-combinator works if I calculate the sum using regular numbers.
Here is my Y-combinator,
(define myY
(lambda (y)
((lambda (x) (x x))
(lambda (x)
(y (lambda (a) ((x x) a)))))))
Here is a recursive function to get the sum of N regular numbers,
(define sum1
(lambda (r)
(lambda (x)
(cond
((zero? x) 0)
(else (+ x (r (sub1 x))))))))
This works
> ((myY sum1) 10)
55
At the bottom of page 15 of the tutorial, the recursive function R=Lrx.Zx0(NS(r(PN))) is used to calculate the sum of N numbers (L=lambda). In this expression, r will be the recursive function, x will be a Church number, Z is the test for zero function, 0 is a zero as a Church number, N is a church number, S is the successor function, P is the predecessor function. I've translated this into Scheme as,
(define myR
(lambda (r)
(lambda (x)
(((myZ x) my0) ((x myS) (r (myP x))))
)))
However, even taking the sum of 0 number blows up.
> ((((myY myR) my0) add1) 0)
No memory!
In the above line, the ((myY myR) my0) should return the Church number zero. I just get the Church number to act on add1 in order to get a human-readable Church number.
Initially, I thought the Y-combinator was the problem, but, as I've shown, this works on recursive functions which use regular numbers. The Church numbers are defined as in the Tutorial and the Scheme versions of the lambda expressions work for arithmetic (addition, multiplication, inequalities).
Edit:
The function myZ is the lambda expression Z=Lx.xF¬F where F=Lxy.y is False (True is T=Lxy.x) and ¬=Lx.xFT is NOT. The function Z implements a conditional test because Z0=T and ZN=F where 0 is the Church number for zero (0=Lxy.y) and N is a non-zero Church number (1=Lxy.xy, 2=Lxy.x(xy) etc.) The Scheme code myZ which implements Z is,
(define myZ
(lambda (x)
(((x myF) myNeg) myF)
))
The lambda calculus only works with normal order reduction (i.e. arguments are only evaluated when their values are needed), and Scheme uses applicative order reduction (arguments are evaluated first), except in "special forms".
Your code works with regular numbers not because of the numbers, but because of cond, which will evaluate at most one of its clauses.
If you replace the cond in your sum1 with a regular function, your computation will not terminate with regular numbers either.
; This does not work
(define (conditional b t e)
(if b t e))
(define sum1
(lambda (r)
(lambda (x)
(conditional
(zero? x)
0
(+ x (r (sub1 x)))))))
You can fix this by adding a level of indirection; suspending evaluation of the branches by wrapping them in functions:
(define sum1
(lambda (r)
(lambda (x)
((conditional
(zero? x)
(lambda (y) 0)
(lambda (y) (+ x (r (sub1 x)))))
"some argument"))))
and the corresponding transformation will work in your implementation.
I found the following code about Higher Order Functions in Scheme:
(define make-double (lambda (f)
(lambda (x)
(f x x))))
(define square (make-double *))
For what I see make-double receives as an argument a function:f, and this function receives and x as argument. This argument x is doubled and make-double return the function f with this x value doubled. Is it like that?
The call to the function square is straightforward, just call the function make-double and the function *, but how can I run this program? When I execute it with:
square
It returns to me:
(lambda (x) (f x x))
How to interpret that? I suppose this function allowed to print an element twice, but maybe I am mistaken? Any help?
Try evaluating (square 42). :-)
Typing square just prints the value of square, which is a function.
(lambda (x) (f x x))
Tells you that square is a function accepting one argument, whose value will be used as both arguments of the function bound by f, which is in this case is *.
I need to write a Scheme higher-order function that takes a function of two parameters as its parameter and returns a curried version of the function. I understand this much so far in terms of curried functions:
(define curriedFunction (lambda (x)
(if (positive? x)
(lambda (y z) (+ x y z))
(lambda (y z) (- x y z)))))
(display ((curriedFunction -5) 4 7))
(display "\n")
(display ((curriedFunction 5) 4 7))
If x is negative, it subtracts x y and z. If x is positive, it adds x, y, and z.
In terms of higher order functions I understand this:
(display (map (lambda (x y) (* x y)) '(1 2 3) '(3 4 5)))
And thirdly I understand this much in terms of passing functions in as arguments:
(define (function0 func x y)
(func x y))
(define myFunction (lambda (x y)
(* x y)))
(display (function0 myFunction 10 4))
In the code directly above, I understand that the function "myFunction" could have also been written as this:
(define (myFunction x y)
(* x y))
So now you know where I am at in terms of Scheme programming and syntax.
Now back to answering the question of writing a Scheme higher-order function that takes a function of two parameters as its parameter and returns a curried version of the function. How do I connect these concepts together? Thank you in advance, I truly appreciate it.
Here is a possible solution:
(define (curry f)
(lambda (x)
(lambda (y)
(f x y))))
The function curry takes the function f and returns a function with a single argument x. That function, given a value for its argument, returns another function that takes an argument y and returns the result of applying the original function f to x and y. So, for instance, (curry +) returns a curried version of +:
(((curry +) 3) 4) ; produces 7
I was just beginning to feel I had a vague understanding of the use of lambda in racket and scheme when I came across the following 'alternate' definitions for cons and car in SICP
(define (cons x y)
(lambda (m) (m x y)))
(define (car z)
(z (lambda (p q) p)))
(define (cdr z)
(z (lambda (p q) q)))
For the life of me I just cannot parse them.
Can anybody explain how to parse or expand these in a way that makes sense for total neophytes?
This is an interesting way to represent data: as functions. Notice that this
definition of cons returns a lambda which closes over the parameters x
and y, capturing their values inside. Also notice that the returned lambda
receives a function m as a parameter:
;creates a closure that "remembers' 2 values
(define (cons x y) (lambda (m) (m x y)))
;recieves a cons holding 2 values, returning the 0th value
(define (car z) (z (lambda (p q) p)))
;recieves a cons holding 2 values, returning the 1st value
(define (cdr z) (z (lambda (p q) q)))
In the above code z is a closure, the same that was created by cons, and in
the body of the procedure we're passing it another lambda as parameter,
remember m? it's just that! the function that it was expecting.
Understanding the above, it's easy to see how car and cdr work; let's
dissect how car, cdr is evaluated by the interpreter one step at a time:
; lets say we started with a closure `cons`, passed in to `car`
(car (cons 1 2))
; the definition of `cons` is substituted in to `(cons 1 2)` resulting in:
(car (lambda (m) (m 1 2)))
; substitute `car` with its definition
((lambda (m) (m 1 2)) (lambda (p q) p))
; replace `m` with the passed parameter
((lambda (p q) p) 1 2)
; bind 1 to `p` and 2 to `q`, return p
1
To summarize: cons creates a closure that "remembers' two values, car
receives that closure and passes it along a function that acts as a selector for
the zeroth value, and cdr acts as a selector for the 1st value. The key
point to understand here is that lambda acts as a
closure.
How cool is this? we only need functions to store and retrieve arbitrary data!
Nested Compositions of car & cdr are defined up to 4 deep in most LISPs. example:
(define caddr (lambda (x) (car (cdr (cdr x)))))
In my view, the definitive trick is reading the definitions from the end to the beginning, because in all three of them the free variables are always those that can be found in the lambda within the body (m, p and q). Here is an attempt to translate the code to English, from the end (bottom-right) to the beginning (top-left):
(define (cons x y)
(lambda (m) (m x y))
Whatever m is, and we suspect it is a function because it appears right next to a (, it must be applied over both x and y: this is the definition of consing x and y.
(define (car z)
(z (lambda (p q) q)))
Whatever p and q are, when something called z is applied, and z is something that accepts functions as its input, then the first one of p and q is selected: this is the definition of car.
For an example of "something that accepts functions as its input", we just need to look back to the definition of cons. So, this means car accepts cons as its input.
(car (cons 1 2)) ; looks indeed familiar and reassuring
(car (cons 1 (cons 2 '()))) ; is equivalent
(car '(1 2)) ; is also equivalent
(car z)
; if the previous two are equivalent, then z := '(1 2)
The last line means: a list is "something that accepts a function as its input".
Don't let your head spin at that moment! The list will only accept functions that can work on list elements, anyway. And this is the case precisely because we have re-defined cons the way that we have.
I think the main point from this exercise is "computation is bringing operations and data together, and it doesn't matter in which order you bring them together".
This should be easy to understand with the combinatory notation (implicitly translated to Scheme as currying functions, f x y = z ==> (define f (λ (x) (λ (y) z)))):
cons x y m = m x y
car z = z _K ; _K p q = p
cdr z = z (_K _I) ; _I x = x _K _I p q = _I q = q
so we get
car (cons x y) = cons x y _K = _K x y = x
cdr (cons x y) = cons x y (_K _I) = _K _I x y = _I y = y
so the definitions do what we expect. Easy.
In English, the cons x y value is a function that says "if you'll give me a function of two arguments I'll call it with the two arguments I hold. Let it decide what to do with them, then!".
In other words, it expects a "continuation" function, and calls it with the two arguments used in its (the "pair") creation.
If I have a list of procedures. How can foldr be used to next the calls?
Like (new abs) => (new (abs x))
Note: foldr should return a procedure.
I have
(define next
(lambda (ls)
(foldr (lambda (x) x) (lambda (x) x) ls)))
But this is giving an error...
The first procedure passed to foldr must have two parameters, like this:
(define next
(lambda (ls)
(foldr (lambda (x a) <???>) ; It's not clear what do you want to do inside
(lambda (x) x) ; this is the identity function, what's it for?
ls)))
Just to be clear:
The first parameter to foldr is the procedure to be executed, and it receives two arguments: the first one is the current value in the list and the second the accumulated value so far
The second parameter to foldr is the initial value, it's suspicious that you're passing the identity function, I bet that's not right
The third parameter to foldr is the list to be processed
The second arg to foldr should be the initial value for the result, typically an empty list.