## Code Along

A code-along is a story about coding in which you are encouraged to “code up” all the examples in the story. In a code-along you should not cut-and-paste.

This code along explores how Boolean expressions can be used to define basic properties and how these properties can be used to create Bricklayer artifacts.

## Property 1

In this example a brick function, called *brickFn*, is created that returns a BLUE brick when it is applied to the origin and returns an EMPTY brick when it is applied to any other coordinate. This behavior is achieved through a conditional expression that performs an equality comparison between the expression consisting of the variable *point*, the formal parameter to *brickFn*, and the tuple value (0,0,0). The function *traverseWithin* is used to traverse the entire virtual space applying *brickFn* to every coordinate encountered and updating each cell visited with the result of the *brickFn* function application. The result, shown in Figure 1, is an artifact consisting of a single BLUE brick placed at the origin.

open Level_5; fun brickFn point = if point = (0,0,0) then BLUE else EMPTY; build (32,32,32); traverseWithin (0,0,0) (31,31,31) brickFn; show "unit brick";

## Property 2

In this example a brick function, called *brickFn*, is created that returns a BLUE brick when it is applied to any coordinate whose y and z values are equal to 0 and returns an EMPTY brick when it is applied to any other coordinate. This behavior is achieved through a conditional expression that performs an equality comparison between the tuple expression (y,z) and the tuple value (0,0). The function *traverseWithin* is used to traverse the entire virtual space applying *brickFn* to every coordinate encountered and updating each cell visited with the result of the *brickFn* function application. The result, shown in Figure 2, is a row of BLUE bricks placed at the following coordinates.

(0,0,0), (1,0,0), … (31,0,0)

open Level_5; fun brickFn (x,y,z) = if (y,z) = (0,0) then BLUE else EMPTY; build (32,32,32); traverseWithin (0,0,0) (31,31,31) brickFn; show "brick row";

## Property 3

In this example a brick function, called *brickFn*, is created that returns a BLUE brick when it is applied to any coordinate containing a 0 and returns an EMPTY brick when it is applied to any other coordinate. This behavior is achieved through a conditional expression that performs the following equality comparison.

x * y * z = 0

Note that the above comparison evaluates to *true* if and only if one or more of the variables in the product x * y * z evaluate to 0.

The function *traverseWithin* is used to traverse the entire virtual space applying *brickFn* to every coordinate encountered and updating each cell visited with the result of the *brickFn* function application. The result, shown in Figure 3, are three BLUE brick walls. The first wall is the xy-plane when z = 0, the second wall is the xz-plane when y = 0, and the third wall is the yz-plane when x = 0. Overlaps occur at the following locations.

- along the x-axis where both y = 0 and z = 0,
- along the y-axis where both x = 0 and z = 0,
- along the z-axis where both x = 0 and y = 0, and
- at the origin where x = 0, y = 0, and z = 0.

open Level_5; fun brickFn (x,y,z) = if x * y * z = 0 then BLUE else EMPTY; build (32,32,32); traverseWithin (0,0,0) (31,31,31) brickFn; show "brick walls";

## Property 4

In this example a brick function, called *brickFn*, is created that returns a BLUE brick when it is applied to any coordinate in the xz-plane where y = 0 whose x-value is a multiple of 5, and returns an EMPTY brick when it is applied to any other coordinate. This behavior is achieved through a conditional expression whose branching behavior is controlled by the following condition.

y = 0 andalso x mod 5 = 0

The function *traverseWithin* is used to traverse the entire virtual space applying *brickFn* to every coordinate encountered and updating each cell visited with the result of the *brickFn* function application. The result, shown in Figure 4, is an artifact consisting of 7 BLUE lines whose x coordinate values are: 0, 5, 10, 15, 20, 25, and 30.

open Level_5; fun brickFn (x,y,z) = if y = 0 andalso x mod 5 = 0 then BLUE else EMPTY; build (32,32,32); traverseWithin (0,0,0) (31,31,31) brickFn; show "vertical bars";

## Property 5

In this example a brick function, called *brickFn*, is created that returns a BLUE brick when it is applied to any coordinate in the xz-plane where y = 0 in which the sum of the x and z values is a multiple of 5; otherwise an EMPTY brick is returned. This behavior is achieved through a conditional expression whose branching behavior is controlled by the following condition.

y = 0 andalso (x + z) mod 5 = 0

The function *traverseWithin* is used to traverse the entire virtual space applying *brickFn* to every coordinate encountered and updating each cell visited with the result of the *brickFn* function application. The result, shown in Figure 5, is an artifact consisting of 13 diagonal BLUE lines.

open Level_5; fun brickFn (x,y,z) = if y = 0 andalso (x + z) mod 5 = 0 then BLUE else EMPTY; build (32,32,32); traverseWithin (0,0,0) (31,31,31) brickFn; show "diagonal bars";

**Question**: Can you modify this program so that the diagonal lines run the other way?

## Property 6

This example shows how a brick function and a traversal can be use to define a function, called *myPut*, whose behavior is equivalent to Bricklayer’s *put* function. Below is an example of equivalent function calls.

myPut (2,3,4) BLUE (0,0,0) ≡ put (2,3,4) BLUE (0,0,0)

The body of the function *myPut* is a let-block whose declarations contain a brick function as well as the start and end points of a traversal. The brick function is called *brickFn* and has a body that is an expression consisting only of the variable *brick*. Note that *brick* is a formal parameter to the *myPut* function. The body of the let-block consists of a traversal. Specifically, the function *traverseWithin* is used to traverse a virtual space covering only the cells which correspond to the shape that would be created by a corresponding *put* function call. The lower left corner of this space is a the coordinate (x,y,z), the *startPoint*, and the upper right corner of this space is equal to the start point plus the brick size. As usual the expression that calculates the coordinate of the upper right corner involves subtracting 1 to avoid an off-by-one error.

The top-level of the program contains a *myPut* function call and a *put* function call. This serves to demonstrate the equivalence between the two function calls.

open Level_5; fun myPut (xSize,ySize,zSize) brick (x,y,z) = let fun brickFn (x,y,z) = brick; val startPoint = (x,y,z); val endPoint = (x + xSize - 1, y + ySize - 1, z + zSize - 1); in traverseWithin startPoint endPoint brickFn end; build (32,32,32); myPut (2,1,4) BLUE (0,0,0); put (2,1,4) RED (2,0,0); show "implementing put";

## Property 7

This example shows how a brick function and a traversal can be use to define a function, called *myPutMultibrick*, whose behavior is equivalent to Bricklayer’s *putMultibrick* function. Below is an example of equivalent function calls.

myPutMultibrick (2,3,4) [BLUE] (0,0,0) ≡ putMultibrick (2,3,4) [BLUE] (0,0,0)

The body of the function *myPutMultibrick* is a let-block whose declarations contain the following.

- a variable called
*newBrick*that is bound to a random brick selection function, - a brick function whose body consists of the call
*newBrick ()*. - the start and end points of a traversal whose shape corresponds to the shape of the brick we want to create.

The trick in this example is to that every call to brickFn will return a randomly selected brick drawn from *brickList*.

The top-level of the program contains a *myPutMultibrick* function call and a *putMultibrick* function call. Visual inspection lets us confirm that the brick selections between the two artifacts are identical. This suggests, but does not imply, that (under the hood) both function calls construct their artifact by visiting (i.e., processing) cells in the same order. Relying on this observed behavior is not recommended. The reason being that neither the *putMultibrick* function nor the *traverseWithin* function provide any guarantees regarding the order in which cells are visited. Furthermore, future improvements to the Bricklayer library may involve modifications to the implementations of the *putMultibrick* and/or *traverseWithin* functions. In this case, it is entirely possible that the sequence in which cells are visited by the *putMultibrick* is different from the sequence of cells visited by the *traverseWithin* function, in which case, the artifacts constructed will no longer be identical.

open Level_5; fun myPutMultibrick (xSize,ySize,zSize) brickList (x,y,z) = let val newBrick = generateRandomBrickFn brickList; fun brickFn (x,y,z) = newBrick (); val startPoint = (x,y,z); val endPoint = (x + xSize - 1, y + ySize - 1, z + zSize - 1); in traverseWithin startPoint endPoint brickFn end; val brickList = [BLUE, INDIGO, VIOLET, ORANGE, DARKRED]; build (32,32,32); myPutMultibrick (5,2,10) brickList (0,0,0); putMultibrick (5,2,10) brickList (6,0,0); show "implementing putMultibrick";

## Property 8

In this example, a function, called *slicedSphere*, is defined that slices a solid sphere using a diagonal plane. Note that this artifact is not easily constructed using Level 4 functions.

The body of the slicedSphere function consists of a let-block whose body first creates a sphere, and traverses the virtual space that contains the sphere applying the function *brickFn* to every cell visited.

The body of *brickFn* consists of a conditional expression that keeps all the cells of the sphere whose height (its y-value) is less-than or equal-to its width (its x-value). The contents of all other cells are discarded (i.e., an EMPTY brick is put in cells having discarded locations). The result is the sliced sphere shown in Figure 8.

open Level_5; val d = 64; val mid = d div 2; fun slicedSphere radius brickList (xCenter,yCenter,zCenter) = let fun brickFn (x,y,z) = if y <= x then IDENTITY else EMPTY; val startPoint = (xCenter - radius, yCenter - radius, zCenter - radius) val endPoint = (xCenter + radius, yCenter + radius, zCenter + radius) in sphere radius brickList (xCenter,yCenter,zCenter); traverseWithin startPoint endPoint brickFn end; val brickList = [BLUE]; build (d,d,d); slicedSphere 20 brickList (mid,mid,mid); show "slicing a sphere";

## Property 9

In this example, a function, called slicedSphere, is defined that creates a horizontal and vertical ring. Note that such an artifact can be constructed using the *ringX* and *ringY* functions. What is different about the code shown here is that these rings are constructed by first creating a hollow sphere and then slicing away portions of it. What remains is the horizontal and vertical ring artifact shown in Figure 8.

The body of the slicedSphere function consits of a let-block whose body first creates a hollow sphere, and traverses the virtual space that contains the hollow sphere applying the function *brickFn* to every cell visited.

The main ideas in our construction method occur in the body of *brickFn*. The body of brickFn is a let-block that declares three variables: *cellContents*, *onHorizontalSlice*, and *onVerticalSlice*. The variable *cellContents* is bound to an expression consisting of the function call *access (x,y,z)*. Recall that the *access* function is a Level_5 function that lets you retrieve (and inspect) the contents of a cell in the virtual space. In this example, if *cellContents* is not equal to EMPTY (i.e., it contains a brick), it implies that the cell is part of the hollow sphere. Knowing this the property satisfied by a horizontal slice can be stated as follows.

A cell belongs to the horizontal slice of a hollow sphere if the cell is not empty and the cell is located on the xz-plane corresponding to the horizontal center of the hollow sphere.

The variable *onHorizontalSlice* is bound to a Boolean expression that captures the above stated property. A similar property can be used to define a vertical slice.

The body of brickFn’s let-block is a conditional expression that checks if the cell, currently being visited, falls on either the horizontal slice or the vertical slice. If it does, then the IDENTITY brick is returned, other wise the EMPTY brick is returned. Recall that the IDENTITY brick is a conceptual brick. Putting an IDENTITY brick at a given location has no effect – the contents of the cell is left unaltered. So what the conditional expression is really saying is the following.

If the current cell belongs to one of our slices, then left its contents alone; otherwise erase the contents of the cell.

open Level_5; val d = 64; val mid = d div 2; fun slicedSphere radius brickList (xCenter,yCenter,zCenter) = let fun brickFn (x,y,z) = let val cellContents = access (x,y,z); val onHorizontalSlice = cellContents <> EMPTY andalso y = yCenter; val onVerticalSlice = cellContents <> EMPTY andalso x = xCenter; in if onHorizontalSlice orelse onVerticalSlice then IDENTITY else EMPTY end val startPoint = (xCenter - radius, yCenter - radius, zCenter - radius) val endPoint = (xCenter + radius, yCenter + radius, zCenter + radius) in hollowSphere radius 10 brickList (xCenter,yCenter,zCenter); traverseWithin startPoint endPoint brickFn end; val brickList = [BLUE, INDIGO, VIOLET, ORANGE, DARKRED]; build (d,d,d); slicedSphere 20 brickList (mid,mid,mid); show "slicing a sphere";