**Due Wed Oct 23, 10:00 am (electronically); papers at recitation.**

Maximum Points: 100

- While we acknowledge that beauty is in the eye of the beholder, you should nonetheless strive for elegance in your code. Not every program which runs deserves full credit. Make sure to state invariants in comments which are sometimes implicit in the informal presentation of an exercise. If auxiliary functions are required, describe concisely what they implement. Do not reinvent wheels and try to make your functions small and easy to understand. Use tasteful layout and avoid longwinded and contorted code. None of the problems requires more than a few lines of SML code.
- Make sure that your file compiles and runs. A program which doesn't run will not get full credit and is likely to incur a heavy penalty.
- Homeworks must be all your own work.
- Late homeworks will be accepted
**only**until start lecture on Thursday, with a 25% penalty. - If you have any questions about the assignment, contact Iliano Cervesato
at
`iliano@cs.cmu.edu`or use`cmu.andrew.academic.15-212-X.discuss`.

You will be asked to implement a functor that realizes the following signature for multisets, when instantiated with the proper arguments.

signature MSET = sig type item (* parameter *) datatype mset = Empty | With of item * mset exception MSet val submset : mset * mset -> bool val eq : mset * mset -> bool val union : mset * mset -> mset val diff : mset * mset -> mset val toString : mset -> string end;This signature can be found in the file mset.sig.

The specifications for the declarations for signature `MSET` are as
follow:

`type item`:

the type of the elements of the multiset. It is a parameter to the signature and will be instantiated by means of a`where`directive.`datatype mset`:

the type of multisets of elements of type`item`. Notice that this type is concrete so that its constructors,`Empty`and`With`are available for manipulation.`exception MSet`:

the exception to be raised if something goes wrong in any of the operations below.`val submset`:

`submset (m1, m2)`returns`true`if every occurrence of an element in`m1`has a distinct corresponding occurrence in`m2`. It returns`false`if either`m1`contains some element that does not appear in`m2`, or`m1`contains more occurrences of an element than`m2`.`val eq`:

`eq (m1, m2)`returns`true`if`m1`contains the same elements and in the same number as`m2`, not necessarily in the same order.`val union`:

`union (m1, m2)`returns the multiset resulting by putting together the elements of`m1`and`m2`. The number of occurrences of any element*e*of the union should be the sum of the number of occurrences of*e*in`m1`and`m2`.`val diff`:

`diff (m1, m2)`returns the multiset resulting by taking away the elements of`m2`from`m1`. If`m2`is a submultiset of`m1`, then the number of occurrences of any element*e*of the difference should be the difference of the number of occurrences of*e*in`m1`and`m2`. If`m2`is not a submultiset of`m1`, exception`MSet`is raised.`val toString`:

`toString m`converts m to a string. The representation of the elements should be separated by commas (`,`) and the overall multiset should be enclosed in braces (`{...}`). In particular the empty multiset should be written`"{}"`.

- a type
`item'`for the elements of the multiset to be constructed, - a function
`itemEq`to test whether two such elements are equal, and - a function
`itemToString`that converts an object of type`item'`to a string,

functor MSet (type item' val itemEq : item' * item' -> bool val itemToString : item' -> string) :> MSET where type item = item' = struct (* ... *) end;Instantiate this functor to obtain multisets of integers and write an expression that prints the result of evaluating

type 'a rewriter = 'a -> 'a exception Fail val THEN : 'a rewriter * 'a rewriter -> 'a rewriter val ID : 'a rewriter val ORELSE : 'a rewriter * 'a rewriter -> 'a rewriter val FAIL : 'a rewriter val TRY : 'a rewriter -> 'a rewriter val REPEAT : 'a rewriter -> 'a rewriterIn this part of the assignment, we will extend this notion in a number of directions:

- We will require that the application of rewrite rules returns a
*validation*that witnesses their use. Every basic rule will be given a name and we will use lists of names as validations: the validation of a rewriting sequence from an expression*e*to an expression*e'*will be the list of the names of the rules used to go from*e*to*e'*.

Building the validation as we apply rules would be a natural idea in our setting, but unfortunately it does not scale up to more general search problems. We will use a different strategy: validations will be generated backwards from the final expression all the way back to the expression we started with. Therefore, whenever a rule*r*is applied to some expression*e*, it will return not only the next expression*e'*, but also a function that maps validations*v'*from*e'*to the final expression*e*to validations_{f}*v*from*e*to*e*. In our case,_{f}*v*will simply be*n*, where_{r}::v'*n*is the name of_{r}*r*. - Our purpose will be to apply rules until some final state is reached. Therefore we need some way to check whether a state resulting from the application of some rule is final. A convenient way to achieve this effect is to use continuations. As we saw in class, a continuation is a function, that will be passed as input to our rewrite rules, and that will tell us what to do next. We will use it to combine basic rules and to check whether we have reached our final state.

type object (* parameter *) type validation = string list type continuation = object * (validation -> validation) -> object * (validation -> validation) type rule = object * continuation -> object * (validation -> validation)Notice that the expressions we want to rewrite, which have type

The following combinators are defined in the signature `REWRITE`:

`exception Fail`

This is the exception to be raised whenever a basic rewrite rule cannot be applied.`val ID : rule`

`ID`rewrites an object`obj`and a continuation`k`, by applying`k`to`obj`and the identity validation.`val FAIL : rule`

`FAIL`is the rewrite rule that always fails, no matter what object-continuation pair it is applied to.`val THEN : rule * rule -> rule`

`r1 THEN r2`(feel free to declare it infix) is the rewrite rule that results from first applying`r1`and then`r2`. Therefore, the application of`r1 THEN r2`to an object-continuation pair`(obj,k)`should call`r1`on`obj`and some continuation`k'`so that`r2`gets applied to the resulting state, say`obj'`, and`k`. Pay particular attention to the manner the validation functions returned by`r1`and`r2`are combined. While implementing this combinator, you might want to take advantage of the predefined infix function`val o : ('b -> 'c) * ('a -> 'b) -> ('a -> 'c)`, where`(f o g)`is the function that applies`g`to its argument and then applies`f`to the result.`val ORELSE : rule * rule -> rule`

`r1 ORELSE r2`(again, feel free to declare it infix) is the rule that behaves like`r1`if this rule is applicable, and otherwise behaves as`r2`.`val REPEAT : rule * int -> rule`

`REPEAT (r, n)`attempts to apply`r`exactly n times. If`n`= 0, it behaves as the identity.`val HOLDS : (object -> bool) -> rule`

`HOLDS p`is the rule that when applied to an object-continuation pair`(obj,k)`behaves as the identity if`(p obj)`is true, and as`FAIL`otherwise.`val UNTIL : rule * (object -> bool) -> rule`

`r UNTIL p`(once more, feel free to declare it infix) is the rule that repeats`r`until a state is reached where`p`holds.

A search function implementing the above specifications will therefore have type

object * rule * object -> validation optionSuch a function, let us call it

The resulting value of this function can be:

`NONE`if every attempt at finding an object that matches`obj2`ends up on a path where`r`is non-applicable. In this case, we know that there is no way of going from`obj1`to`obj2`by means of`r`.`SOME(v)`if a path from`obj1`to`obj2`is found. In this case, the validation`v`is the list of the names of basic rules in`r`that were chained on this path.- non termination!

val depthFirst : object * rule * object -> validation optionfor it.

`depthFirst` is almost immediate to implement on the basis of the
combinators above (do not be scared by the analysis in the previous
paragraph: it is really simple!). It is not satisfactory since a possible
solution might be missed because we made a wrong choice. Fortunately, there
are other search strategies that do not suffer from this problem, although
they are less efficient (and harder to implement!). We will consider here
*iterative deepening*. This strategy works by first checking whether
the initial state `obj1` already matches the target `obj2`. If
this is not the case, it attempts to apply `r` exactly once but in all
possible ways to `obj1`. If no state matching `obj2` is found
in this way, it tries to apply it exactly twice, again in all possible way.
And so on, it checks completely every level of the (implicit) tree
generated by applying the basic constituents of `r` to `obj1`
before moving to the next. In particular, if there is a way to match
`obj2`, then it will find it: this solution will require *n*
applications of `r`, but no attempt will be made to chain `r`
*n+1* times before it has been ascertained that no solution can be
found in *n* moves.

The signature `REWRITE` contains the declaration

val iterativeDeepening : object * rule * object -> validation optionthat will be used to implement iterative deepening.

All the declarations above have been collected in the signature
`REWRITE`, that you can find in the file rewrite.sig. Your task will be to implement a functor
`Rewrite` that, when given

- a type
`object'`for the objects to be rewritten, and - a boolean-valued binary function
`success`, where`success (obj, obj2)`will be used to determine whether the object`obj`matches the specification`obj2`for the final state of the search (although equality is an obvious choice for this function, and it is used in numerous case, it is often convenient to use some other success function, as we will see in the next question),

In order to do so,

- study carefully the implementation of the combinators given in class (remember that all the code is available on-line from the course Web page - http://foxnet.cs.cmu.edu/15-212-X/home.html;
- take inspiration to Question 1 to define
`Rewrite`: the technique is very similar.

signature MSETREWRITE = sig include REWRITE val makeRule : object * object * string -> rule end;

`makeRule (m1, m2, name)` creates a basic rewrite rule that rewrites a
state containing the multiset `m1` to the state that differs from
it for the removal of all elements in `m1` and their replacement with
the elements contained in `m2`. The validation function returned by
applying this rule simply appends `name` to the front of the
validation represented by its argument.

Your task will be to write a functor `MSetRewrite` that accepts as an
argument a structure `M` matching the signature `MSET` of
multisets and constructs a structure satisfying the above signature, in order
to implement rewriting on the objects constructed by means of `MSET`.
You should rely on the functor `Rewrite` implemented in the previous
question for achieving this task. In particular, there is no need to rewrite
adapted versions of the declarations contained in it: ML offers tools to
"inherit" these definitions.

Here is an example:

|| *====* | | +---+ | C | +---+ +---+ | B | | D | +---+ +---+ ==> +---+ | A | | D | | A | ... --------------------- --------------------- |

The final situation on the right (notice that we do not care what happened to
blocks `C` and `D`, as long as they are not on `D`) can
be achieved by having the robot unstack `C` from `B`, put
it down, do the same with `B`, pick up `D` and stack it onto
`A`.

We will be interested in solving precisely the problem in this example. The
structure `B`, defined in the file block.sml,
contains already the declarations for it: you do not need to implement it.

datatype fact = On of B.block * B.block | OnTable of B.block | Clear of B.block | Holding of B.block | ArmEmptyThis declaration, as all the code given below, can be found in the file blockworld.sml.

Your first task will be to define a structure `Situations`
implementing multisets of objects of type situation. Remember that in
Problem 1, you defined a functor that does precisely that. Notice also that
in order to apply it, you need to define functions to test for the equality
of facts (`itemEq`) and to generate a string corresponding to a given
fact (`itemToString`). Call these functions `eqFact` and
`factToString`, respectively.

*stack(b1,b2)*can be applied if the robot is holding*b1*and*b2*is clear from other blocks; it modifies the current situation by having*b1*on*b2*with no block on top of it, and no block in the arm of the robot.

Preconditions:*Holding(b1)*,*clear(b2)*.

Postconditions:*On(b1,b2)*,*EmptyArm*,*clear(b1)*.*putdown(b)*puts*b*on the table.

Preconditions:*Holding(b)*.

Postconditions:*OnTable(b)*,*EmptyArm*,*clear(b)*.*unstack(b1,b2)*fetches*b1*from the top of*b2*.

Preconditions:*On(b1,b2)*,*EmptyArm*,*clear(b1)*.

Postconditions:*Holding(b1)*,*clear(b2)**pickup(b)*picks*b*up from the table.

Preconditions:*OnTable(b)*,*EmptyArm*,*clear(b)*.

Postconditions:*Holding(b)*.

We will instead proceed in a different way and generate on the fly the rules
relevant to the current situation. The idea is to write a single rule that,
when applied to some situation, analyzes it, generates all the moves that are
applicable to it, combines them by means of the `ORELSE` operator, and
applies the resulting rule to the same situation.

Since we want to use `ORELSE` and other combinators, creating a
structure for a multiset rewriting system over situations will be useful. Use
the functor `MSetRewrite` to create this structure, that you will call
`SituationRewrite`.

Let us now proceed with the above plan. In order to generate the rules to be applied in a given situation, the following functions will turn out handy:

`val holding : Situations.mset -> B.block list`.

`holding s`collects the list of every block`b`such that the fact`Holding(b)`occurs in`s`.`val clear : Situations.mset -> B.block list`.

`clear s`makes a similar list with all blocks`b`such that the fact`Clear(b)`occurs in`s`.`val onTable : Situations.mset -> B.block list`.

`onTable s`makes a list with all blocks`b`such that the fact`OnTable(b)`occurs in`s`.`val on : Situations.mset -> (B.block * B.block) list`.

`on s`makes a list with all pairs of blocks`(b1,b2)`such that the fact`On(b1,b2)`occurs in`s`.`val armEmpty : Situations.mset -> bool`.

`armEmpty s`returns`true`if the fact`ArmEmpty`occurs in`s`, and`false`otherwise.

val triplesToTactics : (Situations.mset * Situations.mset * string) list -> SituationRewrite.ruleso that

We will now define four generic rules, `stack`, `putdown`,
`unstack` and `pickup`, that, when applied to a
situation-continuation pair `(s,k)`, generate the `ORELSE`
combination of all rules of the appropriate move type that are applicable to
the situation `s`. As an example, we show the implementation of
`unstack` (for convenience, we opened the structures
`Situations` and `SituationRewrite`:

(* val unstack : SituationRewrite.rule *) fun unstack (sk as (s,k)) = let fun thin' _ nil triples = triples | thin' (bb as (b1,b2)) (b::clears) triples = if B.eq (b1, b) then thin' bb clears ((With(On(bb),With(ArmEmpty,With(Clear(b1),Empty))), With(Holding(b1),With(Clear(b2),Empty)), "unstack(" ^ (B.toString b1) ^ "," ^ (B.toString b2) ^ ")") ::triples) else thin' bb clears triples fun thin nil _ = nil | thin (bb::ons) clears = thin' bb clears (thin ons clears) in if armEmpty s then triplesToTactics (thin (on s) (clear s)) sk else FAIL sk endFollowing a similar pattern, implement

The `ORELSE` combination of these for generic rules is a rule that
computes all the instances of moves applicable to the current state.

*Put your SML code in the handin directory is*

* /afs/andrew/scs/cs/15-212-X/studentdir/<your andrew
id>/ass4*,