My question is about function arguments in conjunction with continuations.
Specifically, what behavior is required, and what is allowed.
Suppose you have a function call (f arg1 arg2 arg3). I realize that
a compliant Scheme implementation is allowed to evaluate the arguments
arg1, arg2, and arg3 in any order. That's fine. But now suppose
that, say, arg2 creates a continuation. In general, some of the other
arguments may be evaluated before arg2 is evaluated, and some may be
evaluated after arg2 is evaluated.
Suppose that, in the Scheme implementation we're using, arg1 is
evaluated before arg2. Further, suppose that f modifies its local
copy of the first argument. Later, when the continuation created
during the evaluation of arg2 is called, arg3 will be evaluated
again and f will be called.
The question is this: When f is called a second time, via the
continuation, what value must/may its first argument have? Does it
need to be the same value that arg1 evaluated to? Or may it be the
modified value from the previous call to f? (Again, this example
assumes that arg1 is evaluated before arg2, but the same issue
applies with different argument evaluation orders. I.e., if arg3 is
evaluated before arg2, then the question applies to arg3.)
I have tried this in a couple of Scheme implementations, and have obtained
differing results. I took into account different orders of evaluation of
the arguments (it's easy to track it by having the argument expressions
log when they're being evaluated). Ignoring that difference, one
implementation always used the original argument values, and another
sometimes used the original argument values, and sometimes used the
modified argument values, depending on whether f was an inline
lambda vs. a global function. Presumably the difference is due to
whether the actual arguments end up being copied into the function's
local variables, or whether they are used in-place.
Here is a version that uses a global function:
(define (bar x cc y)
(set! x (* x 2))
(set! y (* y 3))
(format #t "~a ~a\n" x y)
cc)
(define (foo a b)
(let* ((first #t)
(cb (bar
(+ a 10)
(call/cc (lambda (x) x))
(+ b 100))))
(if first
(begin
(set! first #f)
cb)
(cb '()))))
(define cc (foo 1 2))
(call/cc cc)
(call/cc cc)
The above version uses the original argument values when calling
the function bar in both of the Scheme implementations that I tested.
The function bar sees 11 for the first argument and 102 for the
third argument each time it is called. The output is:
22 306
22 306
22 306
Now, here is a version that replaces the global function with an inline
lambda:
(define (foo a b)
(let* ((first #t)
(cb ((lambda (x cc y)
(set! x (* x 2))
(set! y (* y 3))
(format #t "~a ~a\n" x y)
cc)
(+ a 10)
(call/cc (lambda (x) x))
(+ b 100))))
(if first
(begin
(set! first #f)
cb)
(cb '()))))
(define cc (foo 1 2))
(call/cc cc)
(call/cc cc)
In one of the Scheme implementations I tested (BiwaScheme), this
behaves the same as the previous version. I.e., the called function
always sees the original argument values.
In another Scheme implementation (Gosh/Gauche), this behaves
differently from the previous version. In this case, the called
function uses the modified value of the first argument. In other
words, it handles the inline lambda differently, taking advantage of
the fact that it can see the function definition, and is presumably
using a more direct argument passing mechanism that avoids having to
copy them. Since it isn't copying the arguments, the ones that were
evaluated before the continuation point retain their modified values.
The lambda sees 11 and 102 for the first and third arguments the
first time, then it sees 22 and 102 the second time, and 44 and
102 the third time. So the continuation is picking up the modified
argument values. The output is:
22 306
44 306
88 306
So again, my question is this: Are both behaviors allowed by the
Scheme standard (R6RS and/or R7RS)? Or does Scheme in fact require
that the original argument values be used when the continuation is
invoked?
Update: I originally reported that the Gauche Scheme implementation
gave the three different sets of values shown above. That was true,
but only for certain versions of Gauche. The version I originally
tested was Gauche 0.9.3.3, which shows the three different sets of
values. I later found a site that has three different versions of
Gauche. The oldest, Gauche 0.9.4, also shows the three different
sets of values. But the two newer versions, Gauche 0.9.5 and Gauche
0.9.8, both show the repeated values:
22 306
22 306
22 306
This argues pretty strongly that this was considered a bug which
has since been fixed (just as everyone has been saying).
A continuation will literally create a copy of the stack in the moment of calling call/cc, copy that is also called a control-point. The continuation also stores inside it a copy of the current dynamic environment (more precisely, of the state-space from the dynamic-wind module) and a copy of the thread-local state.
So, when you reactivate the continuation, everything will continue from the moment when it was saved. If some arguments were previously evaluated, their evaluation is saved on the stack and the rest of arguments will be re-evaluated a second time. (as a remark, the dynamic state in scheme is implemented over the dynamic-wind module, so saving the dynamic state involved saving the state of dynamic wind, which is a combination between stack and the state-space (a tree keeping the in-out thunks for dynamic-wind calls)).
The stack starts from the top-level (actually there are other stacklets that represent continuations of the shutdown procedures, but those are touched only when you finish your code), they are not memorized when you call call/cc. So, if in a file/repl you gave 2 expressions, such as
(+ (f 1) 2)
(display "ok")
each of these expressions will have its own stacklet, so saving the continuation within f won't re-evaluate the display.
I think this should be enough to analyse your problem. The arguments are evaluated in unspecified order.
EDIT:
Concerning the answer of foo, for sure it is not correct 22 306 44 306 88 306 but it's correct 22 306 22 306 22 306.
I never used any of these 2 implementations. It is a bug in the implementation that does not bind x after each invocation of the (lambda (x cc y) ...), as the capture of the continuation is made outside the lambda().
The implementation bug seems obvious, it's in their generation of Scode -- they keep x on the stack, despite the fact that set! x was present, which should be an indicator to allocate x as a box on the heap.
While the evaluation order is undefined in the report it is not undefined in an implementtions CPS code. Eg.
(f (+ x 4) (call/cc cont-fun)), where x is a free variable, becomes either:
(call/cc&
cont-fun&
(lambda (v2)
(+& x
4
(lambda (v1)
(f& v1 v2 halt&))))
Or:
(+& x
4
(lambda (v1)
(call/cc&
cont-fun&
(lambda (v2)
(f& v1 v2 halt&)))))
So if the continuation function cont-fun& mutates x this will have an impact of the result in the version that evaluates the arguments right to left since the addition is done in the continuation of it, but in the second version mutating x will not affect the addition since the value is already computed in the passed value v2 and in the event the continuation is captured and rerun this value will never be recomputed. In the first version though you always compute v1 so here mutating the free variable x will affect the result.
If you as a developer wants to avoid this you let* the damn thing:
(let* ((a2 (call/cc cont-fun))
(a1 (+ x 4)))
(f a1 a2))
This code will force the behavior of the addition always being in the continuation of determining a2.
Now I avoided using your mutating examples, but in reality those are just bindings being rerouted. You have overcomplicated bar as the set! does not have any lasting effect. It is always the same as:
(define (bar x cc y)
(format #t "~a ~a\n" (* x 2) (* y 3))
cc)
The continuation caught in:
(bar (+ a 10)
(call/cc (lambda (x) x))
(+ b 100))
Regardless of the order we know the call to bar is the final continuation after evaluating all 3 expressions and then do the body of the let* the first and the 2 consecutive times.
Your second version doesn't change anything since the function doesn't rely on free variables. How the consecutive call to the continuation gave you 44 and 88 is most definitely a compiler optimization that fails. It shouldn't do that. I would have reported it as a bug.
Related
Ok, this is a fairly basic question: I am following the SICP videos, and I am a bit confused about the differences between define, let and set!.
1) According to Sussman in the video, define is allowed to attach a value to avariable only once (except when in the REPL), in particular two defines in line are not allowed. Yet Guile happily runs this code
(define a 1)
(define a 2)
(write a)
and outputs 2, as expected. Things are a little bit more complicated because if I try to do this (EDIT: after the above definitions)
(define a (1+ a))
I get an error, while
(set! a (1+ a))
is allowed. Still I don't think that this the only difference between set! and define: what is that I am missing?
2) The difference between define and let puzzles me even more. I know in theory let is used to bind variables in local scope. Still, it seems to me that this works the same with define, for instance I can replace
(define (f x)
(let ((a 1))
(+ a x)))
with
(define (g x)
(define a 1)
(+ a x))
and f and g work the same: in particular the variable a is unbound outside g as well.
The only way I can see this useful is that let may have a shorter scope that the whole function definition. Still it seems to me that one can always add an anonymous function to create the necessary scope, and invoke it right away, much like one does in javascript. So, what is the real advantage of let?
Your confusion is reasonable: 'let' and 'define' both create new bindings. One advantage to 'let' is that its meaning is extraordinarily well-defined; there's absolutely no disagreement between various Scheme systems (incl. Racket) about what plain-old 'let' means.
The 'define' form is a different kettle of fish. Unlike 'let', it doesn't surround the body (region where the binding is valid) with parentheses. Also, it can mean different things at the top level and internally. Different Scheme systems have dramatically different meanings for 'define'. In fact, Racket has recently changed the meaning of 'define' by adding new contexts in which it can occur.
On the other hand, people like 'define'; it has less indentation, and it usually has a "do-what-I-mean" level of scoping allowing natural definitions of recursive and mutually recursive procedures. In fact, I got bitten by this just the other day :).
Finally, 'set!'; like 'let', 'set!' is pretty straightforward: it mutates an existing binding.
FWIW, one way to understand these scopes in DrRacket (if you're using it) is to use the "Check Syntax" button, and then hover over various identifiers to see where they're bound.
Do you mean (+ 1 a) instead of (1+ a) ? The latter is not syntactically valid.
Scope of variables defined by let are bound to the latter, thus
(define (f x)
(let ((a 1))
(+ a x)))
is syntactically possible, while
(define (f x)
(let ((a 1)))
(+ a x))
is not.
All variables have to be defined in the beginning of the function, thus the following code is possible:
(define (g x)
(define a 1)
(+ a x))
while this code will generate an error:
(define (g x)
(define a 1)
(display (+ a x))
(define b 2)
(+ a x))
because the first expression after the definition implies that there are no other definitions.
set! doesn't define the variable, rather it is used to assign the variable a new value. Therefore these definitions are meaningless:
(define (f x)
(set! ((a 1))
(+ a x)))
(define (g x)
(set! a 1)
(+ a x))
Valid use for set! is as follows:
(define x 12)
> (set! x (add1 x))
> x
13
Though it's discouraged, as Scheme is a functional language.
John Clements answer is good. In some cases, you can see what the defines become in each version of Scheme, which might help you understand what's going on.
For example, in Chez Scheme 8.0 (which has its own define quirks, esp. wrt R6RS!):
> (expand '(define (g x)
(define a 1)
(+ a x)))
(begin
(set! g (lambda (x) (letrec* ([a 1]) (#2%+ a x))))
(#2%void))
You see that the "top-level" define becomes a set! (although just expanding define in some cases will change things!), but the internal define (that is, a define inside another block) becomes a letrec*. Different Schemes will expand that expression into different things.
MzScheme v4.2.4:
> (expand '(define (g x)
(define a 1)
(+ a x)))
(define-values
(g)
(lambda (x)
(letrec-values (((a) '1)) (#%app + a x))))
You may be able to use define more than once but it's not
idiomatic: define implies that you are adding a definition to the
environment and set! implies you are mutating some variable.
I'm not sure about Guile and why it would allow (set! a (+1 a)) but
if a isn't defined yet that shouldn't work. Usually one would use
define to introduce a new variable and only mutate it with set!
later.
You can use an anonymous function application instead of let, in
fact that's usually exactly what let expands into, it's almost
always a macro. These are equivalent:
(let ((a 1) (b 2))
(+ a b))
((lambda (a b)
(+ a b))
1 2)
The reason you'd use let is that it's clearer: the variable names are right next to the values.
In the case of internal defines, I'm not sure that Yasir is
correct. At least on my machine, running Racket in R5RS-mode and in
regular mode allowed internal defines to appear in the middle of the
function definition, but I'm not sure what the standard says. In any
case, much later in SICP, the trickiness that internal defines pose is
discussed in depth. In Chapter 4, how to implement mutually recursive
internal defines is explored and what it means for the implementation
of the metacircular interpreter.
So stick with it! SICP is a brilliant book and the video lectures are wonderful.
The book defines block structure in Chapter 1, allowing you to 'package' defines inside a procedure definition.
Consider this mean-square definition for example:
(define (mean-square x y)
(define (square x) (* x x))
(define (average x y) (/ (+ x y) 2))
(average (square x) (square y)))
when I run (mean-square 2 4) I correctly get 10.
My question is, are the internal definitions ( square and average in this toy case ) run each time I invoke the mean-square procedure via the interpreter? If so, isn't that inefficient? and if not, why?
If the code is somewhat naively compiled, there could be some overhead. The reason is that the inner functions are defined in a brand new lexical environment that is freshly instantiated on each entry into the function. In the abstract semantics, each time the function is called, new lexical closures have to be captured and wired into the correct spots in that environment frame.
Thus it boils down to how much of this can the compiler optimize away. For instance it can notice that neither of the functions actually references the surrounding lexical environment. (The x and y references in these functions are to their own parameters, not to those of the surrounding mean-square). Which means they both be moved to the top level without changing semantics:
(define (__anon1 x) (* x x))
(define (__anon2 x y) (/ (+ x y) 2))
(define (mean-square x y)
(define square __anon1)
(define average __anon2)
(average (square x) (square y)))
And since now square and average are effectively simple aliases (aliases for global entities that are generated by the compiler, which the compiler knows aren't being manipulated by anything outside of its control), the values they denote can be propagated through:
(define (mean-square x y)
(__anon2 (__anon1 x) (__anon1 y)))
It's not a problem. When the mean-square procedure is compiled, all the nested procedures are also compiled. It doesn't need to re-compile them every time you invoke the mean-square procedure.
I think the other answers have probably convinced you that the case you give really doesn't need to have any overhead: the local definitions can be just compiled away. But it's worth thinking about how a system might approach cases where this can't be done.
Consider a definition like this:
(define (make-searcher thing)
(define (search in)
(cond [(null? in)
#f]
[(eqv? (first in) thing)
in]
[else (search (rest in))]))
search)
Well, the local search procedure definitely can't be compiled away here, because it's returned from make-searcher. And it's even worse than that: (make-searcher 1) and (make-searcher 2) need to return different procedures, because ((make-searcher 1) '(1 2 3)) is (1 2 3) while ((make-searcher 2) '(1 2 3)) is (2 3).
So this sounds completely hopeless: the local search procedure not only has to be a procedure (it can't be compiled away), it has to be remade each time.
But in fact things are not nearly so bad. Lexical scope means that the system can know exactly what bindings are visible to search (in this case, a binding for thing as well as its argument). So what you can do, for instance, is compile a bit of code which looks up the values of these bindings in a vector. Then, the thing that is returned from make-search packs together the compiled code of search with a vector of bindings. The compiled code is always the same, only the vector needs to be created and initialised each time.
Imagine this code:
(let ((a expr))
(do-something-with a))
It is the same as:
((lambda (a)
(do-something-with a))
expr)
In an interpreter it might create the lambda each time before calling it while other
languages might turn it into (do-something-with expr). The report doesn't want to touch non functional requirements other than guaranteed tail recursion. In all the serious implementations lambdas are cheap.
Since you mention racket:
File test_com.rkt
#lang racket
(define (mean-square x y)
(define (square x) (* x x))
(define (average x y) (/ (+ x y) 2))
(average (square x) (square y)))
(display (mean-square 2 4))
Terminal commands:
raco make test_com.rkt
raco decompile compiled/test_com_rkt.zo
Resulting output:
(module test_com ....
(require (lib "racket/main.rkt"))
(provide)
(define-values
(mean-square)
(#%closed
mean-square49
(lambda (arg0-50 arg1-51)
'#(mean-square #<path:/home/westerp/compiled/test_com.rkt> 2 0 14 136 #f)
'(flags: preserves-marks single-result)
(/ (+ (* arg0-50 arg0-50) (* arg1-51 arg1-51)) '2))))
(#%apply-values print-values (display '10)) ; the only code that matters!
(void)
(module (test_com configure-runtime) ....
(require '#%kernel (lib "racket/runtime-config.rkt"))
(provide)
(print-as-expression '#t)
(void)))
While mean-square has got their local procedures inlined, because I gave it literal values it will never call it so all it does is (display '10) and then exit.
This is of course if you do make or exe. From DrRacket the language options that enabled debugging and better trace and error messages will run slower.
All of the following are correct. But versoin 2 seems a bit confusing as it suggests an order/sequence of execution, which I think is discouraged in functional progrmaming. So I wonder what is the intuition/benifit of allowing version 2. Is it just for simpler code than versoin 3?
; version 1
(define (foo x)
(cond ((> x 0) 1)))
; version 2
(define (foo x)
(cond ((> x 0) 1 2 3)))
; version 3
(define (foo x)
(cond ((> x 0)
(begin 1 2 3))))
It is not only discouraged, but pointless, for functional programming (either of version 2 or 3). But it is useful if you need to produce side-effects (for example, printing), and version 2 is a bit simpler than version 3.
Scheme isn't a functional language, let alone a non-strictly evaluated one. Scheme directly provides sequenced evaluation of forms. The cond form itself isn't strictly functional: it evaluates the test clauses in strict order, and when it finds one which yields true, it skips the remaining ones. So even without using multiple forms in a single cond clause, we can express imperative programming:
(cond
((> x 10)
(set! y 3))
((< x 0)
(set! z 5)))
The cond form has a long history in Lisp. It was present in some of the earliest versions of Lisp and is described in the 1960 Lisp 1 manual. In that manual, the cond which is described in fact doesn't allow multiple forms: it arguments are strict pairs. It is still that way in the Lisp 1.5 manual. At some point, Lisp dialects started exhibiting the multiple-forms support in cond clauses. Curiously, though, the "cond pair" terminology refuses to die.
The intuition behind allowing (cond (test1 e1 e2 .. en)) is that if you do not provide this, the programmer will get the desired behavior anyway, at the cost of extra verbiage, as your example shows with the explicit begin: another level of parenthesis nesting accompanied by an operator symbol.
It is a backward-compatible extension to the original cond. Allowing the extra forms doesn't change the meaning of cond expressions that were previously correct; it adds meaning to cond expressions that were previously ill-formed.
Other dialects of Lisp, such as Common Lisp and Emacs Lisp, have multiple form evaluation in their cond clauses, so not allowing it in Scheme would only reduce compatibility, adding to someone's workload when they convert code from another dialect to Scheme.
I am a newbie in scheme, and I am in the process of writing a function that checks pairwise disjointess of rules (for the time being is incomplete), I used symbols and lists in order to represent the rues of the grammar. Uppercase symbol is a non-terminal in the grammar, and lowercase is a terminal. I am trying to check if a rule passes the pairwise disjointness test.
I will basically check if a rule has only one unique terminal in it. if it is the case, that rule passes the pairwise disjointness test. In scheme, I am thinking to realize that by representing the terminal symbol in lower case. An example of that rule would be:
'(A <= (a b c))
I will then check the case of a rule that contains an or. like:
'(A <= (a (OR (a b) (a c))))
Finally, I will check recursively for non terminals. A rule for that case would be
'(A <= (B b c))
However, What is keeping me stuck is how to use those symbols as data in order to be processed and recurse upon it. I thought about converting the symbols to strings, but that did not in case of having a list like that for example '(a b c) How can I do it?
Here is what I reached so far:
#lang racket
(define grammar
'(A <= (a A b))
)
(define (pairwise-disjoint lst)
(print(symbol->string (car lst)))
(print( cddr lst))
)
Pairwise Disjoint
As far as I know, the only way to check if a set is pairwise disjoint is to enumerate every possible pair and check for matches. Note that this does not follow the racket syntax, but the meaning should still be pretty clear.
(define (contains-match? x lst)
(cond ((null? x) #f) ; Nothing to do
((null? lst) #f) ; Finished walking full list
((eq? x (car lst)) #t) ; Found a match, no need to go further
(else
(contains-match? x (cdr lst))))) ; recursive call to keep walking
(define (pairwise-disjoint? lst)
(if (null? lst) #f
(let ((x (car lst)) ; let inner vars just for readability
(tail (cdr lst)))
(not
;; for each element, check against all later elements in the list
(or (contains-match? x tail)
(contains-match? (car tail) (cdr tail)))))))
It's not clear to me what else you're trying to do, but this is the going to be the general method. Depending on your data, you may need to use a different (or even custom-made) check for equality, but this works as is for normal symbols:
]=> (pairwise-disjoint? '(a b c d e))
;Value: #t
]=> (pairwise-disjoint? '(a b c d e a))
;Value: #f
Symbols & Data
This section is based on what I perceive to be a pretty fundamental misunderstanding of scheme basics by OP, and some speculation about what their actual goal is. Please clarify the question if this next bit doesn't help you!
However, What is keeping me stuck is how to use those symbols as data...
In scheme, you can associate a symbol with whatever you want. In fact, the define keyword really just tells the interpreter "Whenever I say contains-match? (which is a symbol) I'm actually referring to this big set of instructions over there, so remember that." The interpreter remembers this by storing the symbol and the thing it refers to in a big table so that it can be found later.
Whenever the interpreter runs into a symbol, it will look in its table to see if it knows what it actually means and substitute the real value, in this case a function.
]=> pairwise-disjoint?
;Value 2: #[compound-procedure 2 pairwise-disjoint?]
We tell the interpreter to keep the symbol in place rather than substituting by using the quote operator, ' or (quote ...):
]=> 'pairwise-disjoint?
;Value: pairwise-disjoint?
All that said, using define for your purposes is probably a really poor decision for all of the same reasons that global variables are generally bad.
To hold the definitions of all your particular symbols important to the grammar, you're probably looking for something like a hash table where each symbol you know about is a key and its particulars are the associated value.
And, if you want to pass around symbols, you really need to understand the quote and quasiquote.
Once you have your definitions somewhere that you can find them, the only work that's left to you is writing something like I did above that is maybe a little more tailored to your particular situation.
Data Types
If you have Terminals and Non-Terminals, why not make data-types for each? In #lang racket the way to introduce new data type is with struct.
;; A Terminal is just has a name.
(struct Terminal (name))
;; A Non-terminal has a name and a list of terms
;; The list of terms may contain Terminals, Non-Terminals, or both.
(struct Non-terminal (name terms))
Processing Non-terminals
Now we can find the Terminals in a Non-Terminal's list of terms using the predicate Terminal? which is provided automatically when we define the Terminal as a struct.
(define (find-terminals non-terminal)
(filter Terminal? (Non-terminal-terms non-terminal)))
Pairwise Disjoint Terminals
Once we have filtered the list of terms we can determine properties:
;; List(Terminal) -> Boolean
define (pairwise-disjoint? terminals)
(define (roundtrip terms)
(set->list (list->set terms)))
(= (length (roundtrip terminals)
(length terminals))))
The round trip list->set->list isn't necessarily optimized for speed, of course and profiling actual working implementations may justify refactoring, but at least it's been black-boxed.
Notes
Defining data types with struct provides all sorts of options for validating data as the type is instantiated. If you look at the Racket code base, you will see struct used frequently in the more recent portions.
Since grammar has a list within a list, I think you'll have to either test via list? before calling symbol->string (since, as you discovered, symbol->string won't work on a list), or else you could do something like this:
(map symbol->string (flatten grammar))
> '("A" "<=" "a" "A" "b")
Edit: For what you're doing, i guess the flatten route might not be that helpful. so ya, test via list? each time when parsing and handle accordingly.
I'm trying to find out how I can do an "early return" in a scheme procedure without using a top-level if or cond like construct.
(define (win b)
(let* ((test (first (first b)))
(result (every (lambda (i) (= (list-ref (list-ref b i) i) test))
(enumerate (length b)))))
(when (and (not (= test 0)) result) test))
0)
For example, in the code above, I want win to return test if the when condition is met, otherwise return 0. However, what happens is that the procedure will always return 0, regardless of the result of the when condition.
The reason I am structuring my code this way is because in this procedure I need to do numerous complex checks (multiple blocks similar to the let* in the example) and putting everything in a big cond would be very unwieldy.
Here is how to use call/cc to build return yourself.
(define (example x)
(call/cc (lambda (return)
(when (< x 0) (return #f))
; more code, including possible more calls to return
0)))
Some Schemes define a macro called let/cc that lets you drop some of the noise of the lambda:
(define (example x)
(let/cc return
(when (< x 0) (return #f))
0))
Of course if your Scheme doesn't, let/cc is trivial to write.
This works because call/cc saves the point at which it was called as a continuation. It passes that continuation to its function argument. When the function calls that continuation, Scheme abandons whatever call stack it had built up so far and continues from the end of the call/cc call. Of course if the function never calls the continuation, then it just returns normally.
Continuations don't get truly mind-bending until you start returning them from that function, or maybe storing them in a global data structure and calling them later. Otherwise, they're just like any other language's structured-goto statements (while/for/break/return/continue/exceptions/conditions).
I don't know what your complete code looks like, but it might be better to go with the cond and to factor out the complex checks into separate functions. Needing return and let* is usually a symptom of overly imperative code. However, the call/cc method should get your code working for now.
One way would be to use recursion instead of looping, then an early exit is achieved by not recursing further.
You can use the "call with current continuation" support to simulate a return. There's an example on wikipedia. The function is called call-with-current-continuation, although there's often an alias called call/cc which is exactly the same thing. There's also a slightly cleaner example here
Note: This is quite an advanced Scheme programming technique and can be a bit mind bending at first...!!!!
In this case you don't want a when, you want an if, albeit not top-level.
(define (win b)
(let* ((test (first (first b)))
(result (every (lambda (i) (= (list-ref (list-ref b i) i) test))
(enumerate (length b)))))
(if (and (not (= test 0)) result)
test
0)))
The reason it was always returning zero is that whether or not the body of the when got executed, its result would be dropped on the floor. You see, the lambda implicit in the function define form creates an implicit begin block too, so
(define foo
(lambda (b)
(begin
(let ...)
0)))
and the way begin works is that it returns the result of the last form inside, while dropping all the intermediate results on the floor. Those intermediate results are intended to have side effects. You're not using any of that, which is great(!), but you have to be careful to only have one form (whose result you really want) inside the function definition.
Grem