# CS334: HW 4

#### Table Of Contents

## Instructions

This homework has three types of problems:

**Self Check:**You are strongly encouraged to think about and work through these questions, but you will not submit answers to them.**Problems:**You will turn in answers to these questions.**Pair Programming:**This part involves writing ML code.*You are*You are welcome to choose your own partner, but I can also assist in matching pairs — simply send me a Slack message and I will pair you with someone else also looking for a partner. Please do not let finding a partner go until the last minute.~~required~~**strongly encouraged**to work with a partner on it.

## Reading

**(Required)**Read Mitchell, Chapter 5.**(As necessary)**Read ML references, as needed for the programming questions.

## Problems

### 1. ML Map for Trees (10 pts)

*Mitchell, Problem 5.4*

You are not required to submit a program file for this question, but you may want to double check your answer by running your solution. If you do, be sure to include

`100; Control.Print.printDepth:= `

at the top of your file, so that datatypes print completely.

### 2. ML Reduce for Trees (10 pts)

*Mitchell, Problem 5.5*

You are not required to submit a program file for this question, but you may want to double check your answer by running your solution.

### 3.Currying (10 pts)

*Mitchell, Problem 5.6*

You are not required to submit a program file for this question, but you may want to double check your answer by running your solution.

Note that you MUST explain why the equations hold. One way to do this is to apply both sides of each equation to the same argument(s) and describe how each side evaluates to the same term. For example, show that

\[\tt UnCurry(Curry(f)) (s,t) = f (s,t)\] and \[\tt Curry(UnCurry(g))~s~t = g~s~t\]

for any \(\tt s\) and \(\tt t\).

### 4. Type Inference and Bugs (10 pts)

What is the type of the following ML function?

```
fun append(nil, l) = l
| append(x::l, m) = append(l,m);
```

Write one or two sentences to explain succinctly and informally why `append`

has the type you give. This function is intended to append one list onto another. However, it has a bug. How might knowing the type of this function help the programmer to find the bug?

## Programming

### 1. Random Art (50 pts)

This question and the next are programming questions — you are not required to work with a partner but are encouraged to do so. As always, please send me email if you would like me to help you find a partner.

**Note:** The program you will write generates output files that you will need to view. Thus, I suggest you either install `sml`

on your own computer, or use the lab machines and employ `scp`

to copy the files to your local computer for viewing. (More details below.)

Your GitLab account will have a project for your to use for this question. You can follow the same instructions as on HW 1 for cloning it and adding a partner.

This problem brings together a number of topics we have studied, including grammars, parse trees, and evaluation. Your job is to write an ML program to construct and plot randomly generated functions. The language for the functions can be described by a simple grammar: \[e ::= x ~|~ y ~|~ \sin{\pi e} ~|~ \cos{\pi e} ~|~ (e+e)/2 ~|~ e*e\] Any expression generated by this grammar is a function over the two variables \(x\) and \(y\). Note that any function in this category produces a value between -1 and 1 whenever \(x\) and \(y\) are both in that range.

We can characterize expressions in this grammar with the following ML datatype:

```
datatype Expr =
VarX
| VarYof Expr
| Sine of Expr
| Cosine of Expr * Expr
| Average of Expr * Expr; | Times
```

Note how this definition mirrors the formal grammar given above; for instance, the constructor `Sine`

represents the application of the sin function to an argument multiplied by \(\pi\). Interpreting abstract syntax trees is much easier than trying to interpret terms directly.

**Printing Expressions:**The first two parts require that you edit and run only`expr.sml`

. First, write a function`string exprToString : Expr ->`

to generate a printable version of an expression. For example, calling

`exprToString`

on the expression`Times(Sine(VarX),Cosine(Times(VarX,VarY)))`

should return a string similar to “

`sin(pi*x)*cos(pi*x*y)`

”. The exact details are left to you. (Remember that string concatenation is performed with the`^`

operator.)Test this function on a few sample inputs before moving to the next part.

**Expression Evaluation:**Write the function`real*real -> real eval : Expr ->`

to evaluate the given expression at the given \((x, y)\) location. You may want to use the functions

`Math.cos`

and`Math.sin`

, as well as the floating-point value`Math.pi`

. (Note that an expression tree represented, e.g., as`Sine(VarX)`

corresponds to the mathematical expression \(\sin(\pi x)\), and the`eval`

function must be defined appropriately.)Test this function on a few sample inputs before moving on to the next part. Here are a few sample runs:

`0.5,0.0); - eval (Sine(Average(VarX,VarY))) (val it = 0.707106781187 : real 0.1,0.1); - eval sampleExpr (val it = 0.569335014033 : real`

**Driver Code:**The`art.sml`

file includes the`doRandomGray`

and`doRandomColor`

functions, which generate grayscale and color bitmaps respectively. These functions want to loop over all the pixels in a (by default) 501 by 501 square, which naturally would be implemented by nested for loops. In`art.sml`

, complete the definition of the function`int * int * (int -> unit) -> unit for :`

The argument triple contains a lower bound, an upper bound, and a function; your code should apply the given function to all integers from the lower bound to the upper bound, inclusive. If the greater bound is strictly less than the lower bound, the call to

`for`

should do nothing. Implement this function using imperative features. In other words, use a`ref`

cell and the while construct to build the`for`

function.*Note:*It will be useful to know that you can use the expression form (e1 ; e2) to execute expression e1, throw away its result, and then execute e2. Thus, inside an expression a semicolon acts exactly like comma in C or C++. Also, the expression “`()`

” has type`unit`

, and can be used when you want to “do nothing”.Test your code with a call like the following:

`2, 5, (fn x => (print ((Int.toString(x)) ^ "\n")))); for (`

It should print out the numbers 2,3,4, and 5.

Now produce a grayscale picture of the expression

`sampleExpr`

. You can do this by calling the`emitGrayscale`

function. Look at`doRandomGray`

to see how this function is used.If you get an

`uncaught exception Chr`

error while producing a bitmap, that is an indication that your`eval`

function is returning a number outside the range [-1,1].*Note:*The type assigned to your`for`

function may be more general than the type described above. How could you force it to have the specified type, and why might it be useful to do that? (You don’t need to submit an answer to this, but it is worth understanding.)**Viewing Pictures:**You can view pgm files, as well as the ppm files described below, on a Mac with Preview. When using other computers, or to post them on a web, etc., you might need to first convert the file to jpeg format with the following command:`convert art.pgm art.jpg`

The

`convert`

utility will work for both .ppm and .pgm files. You can install convert on your own machine using the instructions here: https://imagemagick.org/script/download.php. You can also try other image viewing programs, including Gimp.If you have connect to our Unix machines to work, you can copy files back to your own machine with scp, as in the following, where

`~/cs334/lab4/arg.ppm`

specifies the path and file name of the file to copy.`scp freund@limia.cs.williams.edu:~/cs334/lab4/art.ppm .`

Let us know if you have any trouble viewing your artwork!

**Generating Random Expressions:**Your next programming task is to complete the definition of`int * RandomGenerator -> Expr build(depth, rand) :`

The first parameter to

`build`

is a maximum nesting depth that the resulting expression should have. A bound on the nesting depth keeps the expression to a manageable size; it’s easy to write a naive expression generator which can generate incredibly enormous expressions. When you reach the cut-off point (i.e.,`depth`

is 0), you can simply return an expression with no sub-expressions, such as`VarX`

or`VarY`

. If you are not yet at the cut-off point, randomly select one of the forms of`Expr`

and recursively create its subexpressions.The second argument to

`build`

is a function of type . As defined at the top of`art.sml`

, the type`RandomGenerator`

is simply a type abbreviation for a function that takes two integers and returns an integer:`type RandomGenerator = int * int -> int`

Call

`rand(l,h)`

to get a number in the range`l`

to`h`

, inclusive. Successive calls to that function will return a sequence of random values. Documentation in the code describes how to make a`RandomGenerator`

function with`makeRand`

. You may wish to use this function while testing your`build`

function.Once you have completed

`build`

, you can generate pictures by calling the function`int * int * int -> unit doRandomGray :`

which, given a maximum depth and two seeds for the random number generator, generates a grayscale image for a random image in the file

`art.pgm`

. You may also run`int * int * int -> unit doRandomColor :`

which, given a maximum expression depth and two seeds for the random number generator, creates three random functions (one each for red, green, and blue), and uses them to emit a color image

`art.ppm`

. (Note the different filename extension).A few notes:

The build function should not create values of the

`Expr`

datatype directly. Instead, use the build functions`buildX`

,`buildY`

,`buildSine`

, etc. that I have provided in`expr.sml`

. This provides a degree of modularity between the definition of the`Expr`

datatype and the client. We will look at how to enforce this separation with the ML module system in a few more weeks.A depth of 8 – 12 is reasonable to start, but experiment to see what you think is best.

If every sort of expression can occur with equal probability at any point, it is very likely that the random expression you get will be either

`VarX`

or`VarY`

, or something small like`Times(VarX,VarY)`

. Since small expressions produce boring pictures, you must find some way to prevent or discourage expressions with no subexpressions from being chosen “too early”. There are many options for doing this— experiment and pick one that gives you good results.The two seeds for the random number generators determine the eventual picture, but are otherwise completely arbitrary.

**Extensions:**Extend the`Expr`

datatype with at least three more expression forms, and add the corresponding cases to`exprToString`

,`eval`

, and`build`

. The two requirements for this part are that:these expression forms must return a value in the range [-1,1] if all subexpressions have values in this range, and

at least one of the extensions must be constructed out of 3 subexpressions, ie. one of the new

`build`

functions must have type`Expr * Expr * Expr -> Expr`

.

There are no other constraints; the new functions need not even be continuous. Be creative!

Make sure to comment your extensions.

### 2. Expression Representation (10 pts)

This question explores an alternative way of representing expressions in the random art program.

Create a new file `expr-func.sml`

which, like the file `expr.sml`

, defines the `Expr`

representation and basic operations on it. In this version, the definition of the type `Expr`

should be not a datatype, but:

`type Expr = real * real -> real`

That is, instead of the symbolic representation used by `expr.sml`

, this implementation will represent each function in \(x\) and \(y\) directly as an SML function of two `real`

arguments. Redefine the following operations on the new type:

`exprToString`

`eval`

`buildX`

,`buildY`

,`buildSine`

, etc.

The `eval`

function in particular becomes much, much simpler than in `expr.sml`

, but the `exprToString`

function cannot be written successfully, since there is no way to convert an ML function to a string. Thus, your implementation of this function can return something like `"<function>"`

or `"unknown"`

. To test your code, replace

`"expr.sml"; use `

at the top of `art.sml`

with

`"expr-func.sml"; use `

## What To Turn In

#### Problems

Your submitted homework should:

- be clearly written or typed,
- include your name and HW number at the top,
- list any students with whom you discussed the problems,
- be a single PDF file, with one problem per page, and
- be submitted to Gradescope by the due date.

#### Programming

Your submitted programs should:

- be readonably documented and tested,
- list any students with whom you discussed the programming,
- be committed and pushed to your GitLab repository by the due date, and
- be submitted to Gradescope by the due date.

To submit your code in Gradescope, navigate to the submission page for this assignment’s programming component, and select the option to submit files. Then select and upload your source files. If you worked with a partner, only one of you should submit your code, and please indicate who your partner is when you upload your files.

*Note:* The shared repository you are using is either your own or your partners. The other one will be unused. There is no need to do anything to that repository. Our submission scripts will ignore unused repositories and look only at the onces with completed solutions.