I can't seem to wrap my mind around call/cc in Scheme - scheme

Does anyone have a good guide as to how it works? Something with visual aids would be nice, every guide I've come across all seem to say the same thing I need a fresh take on it.

Here's the diagram that was left on our CS lab's whiteboard. So you're going to fetch some apples, and you grab a continuation before you begin. You wander through the forest, collecting apples, when at the end you apply your continuation on your apples. Suddenly, you find yourself where you were before you went into the forest, except with all of your apples.
(display
(call/cc (lambda (k)
(begin
(call-with-forest
(lambda (f)
(k (collect-apples f))))
(get-eaten-by-a-bear)))))
=> some apples (and you're not eaten by a bear)
I think a Bar Mitzvah and buried gold might have been involved.

Have a look at the continuation part of PLAI -- it's very "practical
oriented", and it uses a "black-hole" visualization for continuations that can help you
understand it.

There is no shortcut in learning call/cc. Read the chapters in The Scheme Programming Language or Teach Yourself Scheme in Fixnum Days.

I found that it helps to visualize the call stack. When evaluating an expression, keep track of the call stack at every step. (See for example http://4.flowsnake.org/archives/602) This may be non-intuitive at first, because in most languages the call stack is implicit; you don't get to manipulate it directly.
Now think of a continuation as a function that saves the call stack. When that function is called (with a value X), it restores the saved call stack, then passes X to it.

Never likes visual representation of call/cc as I can't reflect it back to the code (yes, poor imagination) ;)
Anyway, I think it is easier start not with call/cc but with call/ec (escape continuation) if you already familiar with exceptions in other languages.
Here is some code which should evaluate to value:
(lambda (x) (/ 1 x))
What if x will be equal '0'? In other languages we can throw exception, what about scheme?
We can throw it too!
(lambda (x) (call/ec (cont)
(if (= x 0) (cont "Oh noes!") (/ 1 x))))
call/ec (as well as call/cc) is works like "try" here. In imperative languages you can easily jump out of function simply returning value or throwing exception.
In functional you can't jump out, you should evaluate something. And call/* comes to rescue.
What it does it represent expression under "call/ec" as function (this named "cont" in my case) with one argument. When this function is called it replaces the WHOLE call/* to it's argument.
So, when (cont "Oh noes!") replaces (call/ec (cont) (if (= x 0) (cont "Oh noes!") (/ 1 x))) to "Oh noes!" string.
call/cc and call/ec are almost equals to each other except ec simplier to implement. It allows only jump up, whil cc may be jumped down from outside.

Related

How to analyze unknown Common Lisp code with sbcl and slime

How to analyze unknown common lisp source code, in order to understand it?
Given, I have Common Lisp source code of a function. That imaginary function operates on arbitrary and complex data. Now I'd like to analyze such a function.
So, I'm interestend in working methods to analyze some given common lisp source code. I.e. by stepping through source code, like it can be done with elisp source code in Emacs by using edebug-defun.
My tools are slime, sbcl and Emacs.
End of actual question, things below are written only to give a better overview, what this question is about.
Notes about questions background:
I'm not interested in what the above given function does, since
I already know basic common lisp like if, dolist, defun and basics. Also I also know how to query CLHS.
I could probably go through code and data with pen and paper, but why? When I'm sitting in front of a computer.
I already read CL Cookbook - Debugging and Malispers debugging tutorial
Here is an example of such a function, the answer does not need to refer to this function:
(defun wins (grid color)
(declare (optimize (speed 0)
(debug 3)))
(dolist (phase *phase*)
(dolist (start (car phase))
(if (= (* 4 color)
(reduce #'+ (cadr phase)
:key
(lambda (offset)
(aref grid (+ start offset)))))
(return-from wins t)))))
Given above code, for me, it is a mystery when which parts of the arbitrary data is processed and how. It would probably be useful to see it in action.
With that special example, I tried to use (step (wins ...)), but this only stops at (* 4 color), (reduce ... and (* start offset), which is not enough (for me) to understand the function.
Also (trace wins) did not help much.
I took this function from an common lisp teaching book, which already explains it. So there is no need to explain this function.

Little Schemer "S-expression" predicate

Is it true that this is an S-expression?
xyz
asks The Little Schemer. but how to test?
syntactically, i get how to test other statements like
> (atom? 'turkey)
and
> (list? '(atom))
not entirely sure how to test this...
> (list? '(atom turkey) or)
as it just returns...
or: bad syntax in: or
but anyway, knowing how to test for S-expressions is foxing me
so, as per usual, any illumination much appreciated
An "S-expression" is built of atoms via several (possibly zero) cons applications:
(define (sexp? expr)
(or
; several cases:
(atom? expr)
; or
(and (pair? expr) ; a pair is built by a cons
(sexp? (car expr)) ; from a "car"
(sexp? .........)) ; and a "cdr"
)))
This is practically in English. Nothing more to say about it (in code, I mean). Except, after defining the missing
(define (atom? x)
(not (pair? x)))
we see that (sexp? ...) can only return #t. This is the whole point to it: in Lisp, everything is an S-expression – either an atom, or a pair of S-expressions.
The previous answer is correct -- Scheme (and Lisp) are languages that are based on S-expressions. And the provided code is a great start.
But it's not quite correct that everything is an S-expression in those languages. In this case you have an expression that isn't syntactically correct, so the language chokes when it tries to read it in. In other words, it's not an S-expression.
I know it's frustrating, and honestly, not that great of an answer, but this is a great lesson in one of the Golden Rules of Computer Programming: Garbage In, Garbage Out. The fat is that you are going to get these kinds of errors simply because it isn't really feasible, when starting out programming, to test for every possible way that something isn't an S-expression without using the language itself.

Continuation in Scheme

I think I got what a continuations is (in general), but I can't understand how it is used in Scheme.
Consider this example (from wikipedia call/cc)
(define (f return)
(return 2)
3)
(display (call/cc f)) ;=> 2
I cannot understand why:
the continuation is implicit?right?
How is the continuation in this case?
The continuation is the "rest of the computation" that remains to be executed. In your particular example, you could think of this as being (display []) where [] is a hole to be plugged with a value. That is, at the point that call/cc is invoked, what remains to be done is the call to display.
What call/cc does is take this continuation and puts it in a special value that can be applied like a function. It passes this value to its argument (here f). In f, the continuation is bound to return. So (return 2) will basically plug 2 into the continuation, i.e., (display 2).
I don't think this example is actually very helpful, so I think you should read PLAI if you're interested in learning more about continuations (see Part VII). Another good source is these lecture notes by Dan Friedman.

reduce, or explicit recursion?

I recently started reading through Paul Graham's On Lisp with a friend, and we realized that we have very different opinions of reduce: I think it expresses a certain kind of recursive form very clearly and concisely; he prefers to write out the recursion very explicitly.
I suspect we're each right in some context and wrong in another, but we don't know where the line is. When do you choose one form over the other, and what do you think about when making that choice?
To be clear about what I mean by reduce vs. explicit recursion, here's the same function implemented twice:
(defun my-remove-if (pred lst)
(fold (lambda (left right)
(if (funcall pred left)
right
(cons left right)))
lst :from-end t))
(defun my-remove-if (pred lst)
(if lst
(if (funcall pred (car lst))
(my-remove-if pred (cdr lst))
(cons (car lst) (my-remove-if pred (cdr lst))))
'()))
I'm afraid I started out a Schemer (now we're Racketeers?) so please let me know if I've botched the Common Lisp syntax. Hopefully the point will be clear even if the code is incorrect.
If you have a choice, you should always express your computational intent in the most abstract terms possible. This makes it easier for a reader to figure out your intentions, and it makes it easier for the compiler to optimize your code. In your example, when the compiler trivially knows you are doing a fold operation by virtue of you naming it, it also trivially knows that it could possibly parallelize the leaf operations. It would be much harder for a compiler to figure that out when you write extremely low level operations.
I'm going to take a slightly-subjective question and give a highly-subjective answer, since Ira already gave a perfectly pragmatic and logical one. :-)
I know writing things out explicitly is highly valued in some circles (the Python guys make it part of their "zen"), but even when I was writing Python I never understood it. I want to write at the highest level possible, all the time. When I want to write things out explicitly, I use assembly language. The point of using a computer (and a HLL) is to get it to do these things for me!
For your my-remove-if example, the reduce one looks fine to me (apart from the Scheme-isms like fold and lst :-)). I'm familiar with the concept of reduce, so all I need to understand it is figure out your f(x,y) -> z. For the explicit variant, I had to think it for a second: I have to figure out the loop myself. Recursion isn't the hardest concept out there, but I think it is harder than "a function of two arguments".
I also don't care for a whole line being repeated -- (my-remove-if pred (cdr lst)). I think I like Lisp in part because I'm absolutely ruthless at DRY, and Lisp allows me to be DRY on axes that other languages don't. (You could put in another LET at the top to avoid this, but then it's longer and more complex, which I think is another reason to prefer the reduction, though at this point I might just be rationalizing.)
I think maybe the contexts in which the Python guys, at least, dislike implicit functionality would be:
when no-one could be expected to guess the behavior (like frobnicate("hello, world", True) -- what does True mean?), or:
cases when it's reasonable for implicit behavior to change (like when the True argument gets moved, or removed, or replaced with something else, since there's no compile-time error in most dynamic languages)
But reduce in Lisp fails both of these criteria: it's a well-understood abstraction that everybody knows, and that isn't going to change, at least not on any timescale I care about.
Now, I absolutely believe there are some cases where it'd be easier for me to read an explicit function call, but I think you'd have to be pretty creative to come up with them. I can't think of any offhand, because reduce and mapcar and friends are really good abstractions.
In Common Lisp one prefers the higher-order functions for data structure traversal, filtering, and other related operations over recursion. That's also to see from many provided functions like REDUCE, REMOVE-IF, MAP and others.
Tail recursion is a) not supported by the standard, b) maybe invoked differently with different CL compilers and c) using tail recursion may have side effects on the generated machine code for surrounding code.
Often, for certain data structures, many of these above operations are implemented with LOOP or ITERATE and provided as higher-order function. There is a tendency to prefer new language extensions (like LOOP and ITERATE) for iterative code over using recursion for iteration.
(defun my-remove-if (pred list)
(loop for item in list
unless (funcall pred item)
collect item))
Here is also a version that uses the Common Lisp function REDUCE:
(defun my-remove-if (pred list)
(reduce (lambda (left right)
(if (funcall pred left)
right
(cons left right)))
list
:from-end t
:initial-value nil))

Why does this code not work in Scheme?

(define a 42)
(set! 'a 10)
(define a 42)
(define (symbol) 'a)
(set! (symbol) 10)
(define a (cons 1 2))
(set! (car a) 10)
I tried running them in DrScheme and they don't work. Why?
Think of set! is a special form like define which does not evaluate its first operand. You are telling the scheme interpreter to set that variable exactly how you write it. In your example, it will not evaluate the expression 'a to the word a. Instead, it will look for a variable binding named "'a" (or depending on your interpreter might just break before then since I think 'a is not a valid binding).
For the last set of expressions, if you want to set the car of a pair, use the function (set-car! pair val) which works just like any scheme function in that it evaluates all of its operands. It takes in two values, a pair and some scheme value, and mutates the pair so that the car is now pointing to the scheme value.
So for example.
>(define pair (cons 1 2))
>pair
(1 . 2)
>(set-car! pair 3)
(3 . 2)
Because the first argument of set! is a variable name, not an "lvalue", so to speak.
For the last case, use (set-car! a 10).
The issue is with (set! 'a 10), as you shouldn't be quoting the symbol a.
It sounds like you're trying to learn Scheme, and you don't know Lisp, yes? If so, I strongly recommend trying Clojure as an easier to learn Lisp. I failed to grasp the interaction between the reader, evaluation, symbols, special forms, macros, and so forth in both Common Lisp and Scheme because those things all seemed to interact in tangled ways, but I finally really understand them in Clojure. Even though it's new, I found Clojure documentation is actually clearer than anything I found for Scheme or CL. Start with the videos at http://clojure.blip.tv/ and then read the docs at clojure.org.

Resources