Hello guys I am new to Haskell, I would like to create a Haskell Program that can apply DeMorgan's laws on logic expressions. The problem is I can't change the given expression to a new expression (after applying DeMorgan's laws)
To be specific here is my data structure
data LogicalExpression = Var Char
| Neg LogicalExpression
| Conj LogicalExpression LogicalExpression
| Disj LogicalExpression LogicalExpression
| Impli LogicalExpression LogicalExpression
deriving(Show)
I would like to create a function that takes in a "LogicalExpression" and return a "LogicalExpression" after applying DeMorgan's laws.
For example whenever I find this pattern: Neg ( Conj (Var 'a') (Var 'b') ) in a logicalExpression, I need to convert it to Conj ( Neg (Var 'a') Neg (Var 'b') ).
The idea is simple but it's so hard to implement in haskell, it's like trying to make a function (let's call it Z) that searches for x and converts it to y, so if Z is given "vx" it converts it to "vy" only instead of strings it takes in the data structure "logicalExpression" and instead of x it take the pattern I mentioned and spits out the whole logicalExpression again but with the pattern changed.
P.S: I want the function to take any Complex logic expression and simplifies it using DeMorgan's laws
Any hints?
Thanks in advance.
Luke (luqui) has presented probably the most elegant way to think about the problem. However, his encoding requires you to manually get right large swathes of the traversal for each such rewrite rule you want to create.
Bjorn Bringert's compos from A Pattern for Almost Composable Functions can make this easier, especially if you have multiple such normalization passes you need to write. It is usually written with Applicatives or rank 2 types, but to keep things simple here I'll defer that:
Given your data type
data LogicalExpression
= Var Char
| Neg LogicalExpression
| Conj LogicalExpression LogicalExpression
| Disj LogicalExpression LogicalExpression
| Impl LogicalExpression LogicalExpression
deriving (Show)
We can define a class used to hunt down non-top-level sub-expressions:
class Compos a where
compos' :: (a -> a) -> a -> a
instance Compos LogicalExpression where
compos' f (Neg e) = Neg (f e)
compos' f (Conj a b) = Conj (f a) (f b)
compos' f (Disj a b) = Disj (f a) (f b)
compos' f (Impl a b) = Impl (f a) (f b)
compos' _ t = t
For instance, we could eliminate all implications:
elimImpl :: LogicalExpression -> LogicalExpression
elimImpl (Impl a b) = Disj (Not (elimImpl a)) (elimImpl b)
elimImpl t = compos' elimImpl t -- search deeper
Then we can apply it, as luqui does above, hunting down negated conjunctions and disjunctions. And since, as Luke points out, it is probably better to do all your negation distribution in one pass, we'll also include normalization of negated implication and double negation elimination, yielding a formula in negation normal form (assuming that we've already eliminated implication)
nnf :: LogicalExpression -> LogicalExpression
nnf (Neg (Conj a b)) = Disj (nnf (Neg a)) (nnf (Neg b))
nnf (Neg (Disj a b)) = Conj (nnf (Neg a)) (nnf (Neg b))
nnf (Neg (Neg a)) = nnf a
nnf t = compos' nnf t -- search and replace
The key is the last line, which says that if none of the other cases above match, go hunt for subexpressions where you can apply this rule. Also, since we push the Neg into the terms, and then normalize those, you should only wind up with negated variables at the leaves, since all other cases where Neg precedes another constructor will be normalized away.
The more advanced version would use
import Control.Applicative
import Control.Monad.Identity
class Compos a where
compos :: Applicative f => (a -> f a) -> a -> f a
compos' :: Compos a => (a -> a) -> a -> a
compos' f = runIdentity . compos (Identity . f)
and
instance Compos LogicalExpression where
compos f (Neg e) = Neg <$> f e
compos f (Conj a b) = Conj <$> f a <*> f b
compos f (Disj a b) = Disj <$> f a <*> f b
compos f (Impl a b) = Impl <$> f a <*> f b
compos _ t = pure t
This doesn't help in your particular case here, but is useful later if you need to return multiple rewritten results, perform IO, or otherwise engage in more complicated activities in your rewrite rule.
You might need to use this, if for instance, you wanted to try to apply the deMorgan laws in any subset of the locations where they apply rather than pursue a normal form.
Notice that no matter what function you are rewriting, Applicative you are using, or even directionality of information flow during the traversal, the compos definition only has to be given once per data type.
If I understand correctly, you want to apply De Morgan's laws to push the negation down into the tree as far as possible. You'll have to explicitly recurse down the tree many times:
-- no need to call self on the top-level structure,
-- since deMorgan is never applicable to its own result
deMorgan (Neg (a `Conj` b)) = (deMorgan $ Neg a) `Disj` (deMorgan $ Neg b)
deMorgan (Neg (a `Disj` b)) = (deMorgan $ Neg a) `Conj` (deMorgan $ Neg b)
deMorgan (Neg a) = Neg $ deMorgan a
deMorgan (a `Conj` b) = (deMorgan a) `Conj` (deMorgan b)
-- ... etc.
All of this would be much easier in a term-rewriting system, but that's not what Haskell is.
(Btw., life becomes a lot easier if you translate P -> Q into not P or Q in your formula parser and remove the Impli constructor. The number of cases in each function on formulas becomes smaller.)
Others have given good guidance. But I would phrase this as a negation eliminator, so that means you have:
deMorgan (Neg (Var x)) = Neg (Var x)
deMorgan (Neg (Neg a)) = deMorgan a
deMorgan (Neg (Conj a b)) = Disj (deMorgan (Neg a)) (deMorgan (Neg b))
-- ... etc. match Neg against every constructor
deMorgan (Conj a b) = Conj (deMorgan a) (deMorgan b)
-- ... etc. just apply deMorgan to subterms not starting with Neg
We can see by induction that in the result, Neg will only be applied to Var terms, and at most once.
I like to think of transformations like this as eliminators: i.e. things that try to "get rid" of a certain constructor at the top level by pushing them down. Match the constructor you are eliminating against every inner constructor (including itself), and then just forward the rest. For example, a lambda calculus evaluator is an Apply eliminator. An SKI converter is a Lambda eliminator.
The important point is the recursive application of deMorgan. It is quite different from (for example) :
deMorgan' z#(Var x) = z
deMorgan' (Neg (Conj x y)) = (Disj (Neg x) (Neg y))
deMorgan' (Neg (Disj x y)) = (Conj (Neg x) (Neg y))
deMorgan' z#(Neg x) = z
deMorgan' (Conj x y) = Conj x y
deMorgan' (Disj x y) = Disj x y
which does not work :
let var <- (Conj (Disj (Var 'A') (Var 'B')) (Neg (Disj (Var 'D') (Var 'E'))))
*Main> deMorgan' var
Conj (Disj (Var 'A') (Var 'B')) (Neg (Disj (Var 'D') (Var 'E')))
The problem here is that you do not apply transformations in the subexpressiosn (the x and ys).
Related
I'm working my way through SICP, and it gives the following definition for zero for Church Numerals:
(define zero (lambda (f) (lambda (x) x)))
I have a few questions about that:
Why the complicated syntax? It seems to be quite readable by just having the following instead:
(define (zero f)
(lambda (x) x))
where we can see it's a function called zero that takes one (unused) argument f and returns a function-of-one-parameter that will return its parameter. It almost seems like the definition is just intended to be as non-straightforward as possible.
What is the x there for? For example doing something like:
((zero square) 100)
returns 100. Is x just the default value returned?
There is no x in (lambda (x) x). None.
The x in (lambda (x) x) is bound. It could be named by any name whatever. We can not talk about x in (lambda (x) x) any more than we could talk about y in (lambda (y) y).
There is no y in (lambda (y) y) to speak of. It is just a placeholder, an arbitrary name whose sole purpose in the body is to be the same as in the binder. Same, without regard for which specific name is used there as long as it is used twice -- first time in the binder, and the other time in the body.
And in fact there is this whole 'nother notation for lambda terms, called De Bruijn notation, where the same whole thing is written (lambda 1). With 1 meaning, "I refer to the argument which the binder 1 step above me receives".
So x is unimportant. What's important is (lambda (x) x) which denotes a function which returns its argument as is. The so called "identity" function.
But even this is not important here. The Church encoding of a number is really a binary function, a function expecting two arguments -- the f and the z. The "successor step" unary function f and the "zero" "value" z, whatever that might be, as long as the two go together. Make sense together. Work together.
So how come we see two unary functions there when it is really one binary function in play?
That is the important bit. It is known as currying.
In lambda calculus all functions are unary. And to represent a binary function an unary function is used, such that when given its (first) argument it returns another unary function, which, when given its (now, second) argument, performs whatever thing our intended binary function ought to perform, using those two arguments, the first and the second.
This is all very very simple if we just write it in combinatory (equational) notation instead of the lambda notation:
zero f z = z
one f z = f z
two f z = f (f z) = f (one f z) = succ one f z
succ one f z = f (one f z)
where every juxtaposition denotes an application, and all applications associate on the left, so we imagine the above being a shortcut notation for
zero f = lambda z. z
zero = lambda f. (lambda z. z)
......
......
succ = lambda one. (lambda f. (lambda z. f (one f z) ))
;; such that
succ one f z = (((succ one) f) z)
= ((((lambda one. (lambda f. (lambda z. f (one f z) ))) one) f) z)
= ....
= (f ((one f) z))
= f (one f z)
but it's the same thing. The differences in notation are not important.
And of course there is no one in lambda one. (lambda f. (lambda z. f (one f z) )). It is bound. It could just be named, I dunno, number:
succ number f z = f (number f z) = f ((number f) z)
meaning, (succ number) is such a number, which, given the f and the z, does with them one more f step compared to what number would do.
And so, ((zero square) 100) means, use the number zero with the successor step square and the zero value of 100, and have zero perform its number of successor steps for us -- that is to say, 0 steps -- starting from the zero value. Thus returning it unchanged.
Another possible use is ((zero (lambda (x) 0)) 1), or in general
((lambda (n) ((n (lambda (x) 0)) 1)) zero)
;; or even more generally, abstracting away the 0 and the 1,
((((lambda (n) (lambda (t) (lambda (f) ((n (lambda (x) f)) t)))) zero) 1) 0)
which is just another way of writing
zero (lambda x. 0) 1 ;; or
foo n t f = n (lambda x. f) t ;; and calling
foo zero 1 0
Hopefully you can see what foo is, easily. And also how to read aloud this t and this f. (Probably the original f would be better named s, for "successor", or something like that).
(define( app list1 list2)
(if(empty? list1) list2
(cons (car list1) (app(cdr list1)list2))))
(app ((list "↔" "→" "∧" "⊕" "∨" "¬")) (list "P" "Q" "R" "S" "U" "X" "Y" "Z"))
(define L (list "↔" "→" "∧" "⊕" "∨" "¬"))
(define ( f L n)
(if (= n 0) "p"
(string-append "p" (car L) (f(cdr L) (- n 1)))))
(f L 3)
You have the following recursive definition of a PROPOSITION:
T, F are propositions ( truth value of propositional variables)
List item
Propositional letters P, Q , R, S, U, X, Y, Z are propositions.
If A is a proposition the ¬A is a proposition.
If A and B are propositions then
A⊕B , A→B , A∧B, A∨B , A↔B are propositions.
Write a DrRacket procedure that will randomly generate a proposition with a given number of operations .
I could not complete the function. Can you help me please?
Since this is a homework question, I'll show you the techinque using a different example. The following grammar has two non-terminals S and T. For each non-terminal I have defined a function that generates a random string according to the rules. Since S has four rules, I pick one of the rules at random.
#lang racket
;;; Grammar
; The grammar has two non-terminals S and T.
; There are four rules for S and one for T.
; S -> aSa
; S -> bSb
; S -> cT
; S -> ε
; T -> dS
(define (S)
(case (random 4) ; random number 0,1,2,3 (four rules for S)
[(0) (string-append "a" (S) "a")]
[(1) (string-append "b" (S) "b")]
[(2) (string-append "c" (T))]
[(3) ""]))
(define (T)
; only one rule, so no need for random here
(string-append "d" (S)))
; generate a random string according to the grammar
(S)
Some example outputs:
"bb"
"bbcdbcdbbb"
"cdbb"
This is from the MIT 6.001 Online Tutor, it's part of the third problem set.
Question: Indicate the type of each of the following expressions. If you need type variables, use A,B,C, etc., starting with A as the leftmost variable.
(lambda (x y) x) = A,B->A
(lambda (p) (p 3))
(lambda (p x) (p x)) = (A->B), A->B
(lambda (x y comp) (if (comp x y) x y))
As you can see I solved 1 and 3, but that was mainly out of luck. I still am having issues with understanding the concept and that is stopping me from solving 2 and 4.
Lecture slides can be found here (view the last few).
A, B -> A
(number -> A) -> A
(A -> B), A -> B
A, A, (A, A -> boolean) -> A
(the last assumes that x and y are the same types)
I am getting stuck with the Wikipedia description of the predecessor function in lambda calculus.
What Wikipedia says is the following:
PRED := λn.λf.λx. n (λg.λh. h (g f)) (λu.x) (λu.u)
Can someone explain reduction processes step-by-step?
Thanks.
Ok, so the idea of Church numerals is to encode "data" using functions, right? The way that works is by representing a value by some generic operation you'd perform with it. We can therefore go in the other direction as well, which can sometimes make things clearer.
Church numerals are a unary representation of the natural numbers. So, let's use Z to mean zero and Sn to represent the successor of n. Now we can count like this: Z, SZ, SSZ, SSSZ... The equivalent Church numeral takes two arguments--the first corresponding to S, and second to Z--then uses them to construct the above pattern. So given arguments f and x, we can count like this: x, f x, f (f x), f (f (f x))...
Let's look at what PRED does.
First, it creates a lambda taking three arguments--n is the Church numeral whose predecessor we want, of course, which means that f and x are the arguments to the resulting numeral, which thus means that the body of that lambda will be f applied to x one time fewer than n would.
Next, it applies n to three arguments. This is the tricky part.
The second argument, that corresponds to Z from earlier, is λu.x--a constant function that ignores one argument and returns x.
The first argument, that corresponds to S from earlier, is λgh.h (g f). We can rewrite this as λg. (λh.h (g f)) to reflect the fact that only the outermost lambda is being applied n times. What this function does is take the accumulated result so far as g and return a new function taking one argument, which applies that argument to g applied to f. Which is absolutely baffling, of course.
So... what's going on here? Consider the direct substitution with S and Z. In a non-zero number Sn, the n corresponds to the argument bound to g. So, remembering that f and x are bound in an outside scope, we can count like this: λu.x, λh. h ((λu.x) f), λh'. h' ((λh. h ((λu.x) f)) f) ... Performing the obvious reductions, we get this: λu.x, λh. h x, λh'. h' (f x) ... The pattern here is that a function is being passed "inward" one layer, at which point an S will apply it, while a Z will ignore it. So we get one application of f for each S except the outermost.
The third argument is simply the identity function, which is dutifully applied by the outermost S, returning the final result--f applied one fewer times than the number of S layers n corresponds to.
McCann's answer explains it pretty well. Let's take a concrete example for Pred 3 = 2:
Consider expression: n (λgh.h (g f)) (λu.x). Let K = (λgh.h (g f))
For n = 0, we encode 0 = λfx.x, so when we apply the beta reduction for (λfx.x)(λgh.h(gf)) means (λgh.h(gf)) is replaced 0 times. After further beta-reduction we get:
λfx.(λu.x)(λu.u)
reduces to
λfx.x
where λfx.x = 0, as expected.
For n = 1, we apply K for 1 times:
(λgh.h (g f)) (λu.x)
=> λh. h((λu.x) f)
=> λh. h x
For n = 2, we apply K for 2 times:
(λgh.h (g f)) (λh. h x)
=> λh. h ((λh. h x) f)
=> λh. h (f x)
For n = 3, we apply K for 3 times:
(λgh.h (g f)) (λh. h (f x))
=> λh.h ((λh. h (f x)) f)
=> λh.h (f (f x))
Finally, we take this result and apply an id function to it, we got
λh.h (f (f x)) (λu.u)
=> (λu.u)(f (f x))
=> f (f x)
This is the definition of number 2.
The list based implementation might be easier to understand, but it takes many intermediate steps. So it is not as nice as the Church's original implementation IMO.
After Reading the previous answers (good ones), I’d like to give my own vision of the matter in hope it helps someone (corrections are welcomed). I’ll use an example.
First off, I’d like to add some parenthesis to the definition that made everything clearer to me. Let’s redifine the given formula to:
PRED := λn λf λx.(n (λgλh.h (g f)) (λu.x)) (λu.u)
Let’s also define three Church numerals that will help with the example:
Zero := λfλx.x
One := λfλx. f (Zero f x)
Two := λfλx. f (One f x)
Three := λfλx. f (Two f x)
In order to understand how this works, let's focus first on this part of the formula:
n (λgλh.h (g f)) (λu.x)
From here, we can extract this conclusions:
n is a Church numeral, the function to be applied is λgλh.h (g f) and the starting data is λu.x
With this in mind, let's try an example:
PRED Three := λf λx.(Three (λgλh.h (g f)) (λu.x)) (λu.u)
Let's focus first on the reduction of the numeral (the part we explained before):
Three (λgλh.h (g f)) (λu.x)
Which reduces to:
(λgλh.h (g f)) (Two (λgλh.h (g f)) (λu.x))
(λgλh.h (g f)) ((λgλh.h (g f)) (One (λgλh.h (g f)) (λu.x)))
(λgλh.h (g f)) ((λgλh.h (g f)) ((λgλh.h (g f)) (Zero (λgλh.h (g f)) (λu.x))))
(λgλh.h (g f)) ((λgλh.h (g f)) ((λgλh.h (g f)) ((λfλx.x) (λgλh.h (g f)) (λu.x)))) -- Here we lose one application of f
(λgλh.h (g f)) ((λgλh.h (g f)) ((λgλh.h (g f)) (λu.x)))
(λgλh.h (g f)) ((λgλh.h (g f)) (λh.h ((λu.x) f)))
(λgλh.h (g f)) ((λgλh.h (g f)) (λh.h x))
(λgλh.h (g f)) (λh.h ((λh.h x) f))
(λgλh.h (g f)) (λh.h (f x))
(λh.h ((λh.h (f x) f)))
Ending up with:
λh.h f (f x)
So, we have:
PRED Three := λf λx.(λh.h (f (f x))) (λu.u)
Reducing again:
PRED Three := λf λx.((λu.u) (f (f x)))
PRED Three := λf λx.f (f x)
As you can see in the reductions, we end up applying the function one time less thanks to a clever way of using functions.
Using add1 as f and 0 as x, we get:
PRED Three add1 0 := add1 (add1 0) = 2
Hope this helps.
You can try to understand this definition of the predecessor function (not my favourite one) in terms of continuations.
To simplify the matter a bit, let us consider the following variant
PRED := λn.n (λgh.h (g S)) (λu.0) (λu.u)
then, you can replace S with f, and 0 with x.
The body of the function iterates n times a transformation M over an argument N. The argument N is a function of type (nat -> nat) -> nat that expects a continuation for nat and returns a nat. Initially, N = λu.0, that is it ignores the continuation and just returns 0.
Let us call N the current computation.
The function M: (nat -> nat) -> nat) -> (nat -> nat) -> nat modifies the computation g: (nat -> nat)->nat as follows.
It takes in input a continuation h, and applies it to the
result of continuing the current computation g with S.
Since the initial computation ignored the continuation, after one application of M we get the computation (λh.h 0), then (λh.h (S 0)), and so on.
At the end, we apply the computation to the identity continuation
to extract the result.
I'll add my explanation to the above good ones, mostly for the sake of my own understanding. Here's the definition of PRED again:
PRED := λnfx. (n (λg (λh.h (g f))) ) λu.x λu.u
The stuff on the right side of the first dot is supposed to be the (n-1) fold composition of f applied to x: f^(n-1)(x).
Let's see why this is the case by incrementally grokking the expression.
λu.x is the constant function valued at x. Let's just denote it const_x.
λu.u is the identity function. Let's call it id.
λg (λh.h (g f)) is a weird function that we need to understand. Let's call it F.
Ok, so PRED tells us to evaluate the n-fold composition of F on the constant function and then to evaluate the result on the identity function.
PRED := λnfx. F^n const_x id
Let's take a closer look at F:
F:= λg (λh.h (g f))
F sends g to evaluation at g(f).
Let's denote evaluation at value y by ev_y.
That is, ev_y := λh.h y
So
F = λg. ev_{g(f)}
Now we figure out what F^n const_x is.
F const_x = ev_{const_x(f)} = ev_x
and
F^2 const_x = F ev_x = ev_{ev_x(f)} = ev_{f(x)}
Similarly,
F^3 const_x = F ev_{f(x)} = ev_{f^2(x)}
and so on:
F^n const_x = ev_{f^(n-1)(x)}
Now,
PRED = λnfx. F^n const_x id
= λnfx. ev_{f^(n-1)(x)} id
= λnfx. id(f^(n-1)(x))
= λnfx. f^(n-1)(x)
which is what we wanted.
Super goofy. The idea is to turn doing something n times into doing f n-1 times. The solution is to apply F n times to const_x to obtain
ev_{f^(n-1)(x)} and then to extract f^(n-1)(x) by evaluating at the identity function.
Split this definition
PRED := λn.λf.λx.n (λg.λh.h (g f)) (λu.x) (λu.u)
into 4 parts:
PRED := λn.λf.λx. | n | (λg.λh.h (g f)) | (λu.x) | (λu.u)
- --------------- ------ ------
A B C D
For now, ignore D. By definition of Church numerals, A B C is B^n C: Apply n folds of B to C.
Now treat B like a machine that turns one input into one output. Its input g has form λh.h *, when appended by f, becomes (λh.h *) f = f *. This adds one more application of f to *. The result f * is then prepended by λh.h to become λh.h (f *).
You see the pattern: Each application of B turns λh.h * into λh.h (f *). If we had λh.h x as the begin term, we would have λh.h (f^n x) as the end term (after n applications of B).
However, the begin term is C = (λu.x), when appended by f, becomes (λu.x) f = x, then prepended by λh.h to become λh.h x. So we had λh.h x after, not before, the first application of B. This is why we have λh.h (f^(n-1) x) as the end term: The first application of f was ignored.
Finally, apply λh.h (f^(n-1) x) to D = (λu.u), which is identity, to get f^(n-1) x. That is:
PRED := λn.λf.λx.f^(n-1) x
I'm making a game, and I have this:
(define b "black piece") (define w "white piece")
(define (board)
(lambda (matrix)
(list ((b w b w b w b w)
(w b w b w b w b)
(b w b w b w b w)
(w b w b w b w b)
(b w b w b w b w)
(w b w b w b w b)
(b w b w b w b w)
(w b w b w b w b)))))
board makes a list with 8 lines and 8 columns of black and white pieces.
How do I access and change elements of the board? How do I do the procedure matrix with recursion?
first a few notes:
(define f (lambda (x) l ))
is the same as
(define (f x) l ))
You however are combining them with
(define (board) (lambda (matrix) l ))
which is the same as
(define board (lambda () (lambda (matrix) l )))
The distinction is important. The first two I have listed bind f to a function that take one parameter and return l. I'm guessing this is what you want to do. In the second two, you're binding board to a function that takes no parameters and returns a function that takes 1 parameter, matrix, (which it doesn't seem to do anything with), and returns a l.
second issue, (list ((b w....) ...)) isn't going to work because it will try to evaluate (b w ...). you need to have list in the function application position for each row of your board like so (list (list b w ...) (list w b ...) ...) in order for you code to even compile.
On to your question. link-ref is included in racket/base and is used for referencing elements in a list when you know the index into the list.
(list-ref 2 (list 'a 'b 'c 'd))
will return 'c. The index starts at 0. Since you have a list of lists, you will need to apply list-ref twice to retrieve a 'b or 'w.
As for changing it, well, you can't. As of r6rs, pairs (which make up lists) are immutable. The recommended way of doing things when possible is to return a new list with your change. you can use this somewhat inefficient version of list-set which returns a copy of the list with your new value at an index.
(define (list-set lis idx val)
(map (lambda (e i)
(if (= i idx) val e))
lis
(iota (length lis))))
In this case however, I would recommend switching to a different data structure more appropriate to the task at hand since you probably want O(1) access to the elements in the board. Look into vectors which behave much like lists but are used for constant lookups and updates. there is a built in vector-ref and vector-set! operations, which you should use instead of my above function.
Incase this is part of a larger problem and you're already using lists everywhere, you can use the vector->list and list->vector functions to go back and forth. Also, you can use mutable lists but don't.
Better still is the multidimensional array library offered in srfi/25, but that might be more complicated that you want to get.
The second part of your question was how to construct the board recursively. Well, here's a version using map.
(require (lib "1.ss" "srfi"))
(define (board)
(map (lambda (x)
(map (lambda (y)
(if (odd? (+ x y)) b w))
(iota 8)))
(iota 8)))
and here's a recursive version
(define (board)
(letrec ((board-helper
(lambda (x)
(if (eq? x 8) '()
(cons (row-helper x 0) (board-helper (+ 1 x))))))
(row-helper
(lambda (x y)
(if (eq? y 8) '()
(cons (if (odd? (+ x y)) b w) (row-helper x (+ 1 y)))))))
(board-helper 0)))