Working with Abstractions
This assignment is due March 15 at 11:59 PM. Pull the Github repository and start working on Assignment2.hs. This homework is based on that of Niki Vazou and Brent Yorgey.
Maps and Folds
We’ve seen mapping and folding on lists. Here you are required to port these patterns to trees. A Tree is either empty or contains a value and two subtrees.
data Tree a = Nil | Node a (Tree a) (Tree a) deriving (Eq, Show)
For testing, we can define few trees.
t0 = Node 1 (Node 2 Nil Nil) Nil t1 = Node 0 t0 t0 t2 = Node "a" (Node "see" Nil Nil) (Node "tree" Nil Nil)
Define a
mapTfunction that maps a function over all elements of a tree. Here are some examples of howmapTworks.mapT ((+) 1) t0 == Node 2 (Node 3 Nil Nil) Nil mapT ((++) "!") t2 == Node "!a" (Node "!see" Nil Nil) (Node "!tree" Nil Nil)
Define a function
foldTthat folds monoidal elements of a tree, top-to-bottom and left-to-right. Here are some examples.foldT t2 == "seeatree" getSum . foldT $ mapT Sum t1 == 6 getProduct . foldT $ mapT Product t1 == 0
Here we’re using the
SumandProductconstructors fromData.Monoid. These are wrappers overIntegerthat determine the monoidal operation. In the case ofSumit’s+, andProductis*.Use your
foldTfunction to define a functionelemsTthat returns the list of all the elements of a tree. Note that it is fine for the resulting list to contain duplicate elements.elemsT t0 == [2, 1] elemsT t2 == ["see", "a", "tree"]
Calculator
Consider a small calculator language.
data ExprT = Lit Integer | Add ExprT ExprT | Mul ExprT ExprT
For testing purposes, we will use the following small examples.
e0 = (Lit 3) e1 = (Add (Lit 1) (Lit 1)) e2 = (Add (Lit 1) (Mul (Lit 2) (Lit 2)))
Write a function
evalthat evaluates an expression in this language.eval e0 == 3 eval e1 == 2 eval e2 == 5
Make
ExprTan instance of theShowtypeclass.show e0 == "3" show e1 == "(1+1)" show e2 == "(1+(2*2))"
Create the
Exprtypeclass with the functionslit,add, andmulwhich parallel the constructors ofExprT. Consider the following example.e = mul (add (lit 2) (lit 3)) (lit 4)
If you ask for the type of
eyou’ll getExpr a => a. This means the type ofecould be anyExpr. We can use a type annotation to enforce a particular interpretation, but that requires making some instances ofExpr.Now make each of the following types an instance of
Expr.ExprTin the obvious way.e :: ExprT == Mul (Add (Lit 2) (Lit 3)) (Lit 4)
Integerthat works like the original calculator.e :: Integer == 20
Boolwhere every literal value less than or equal to0is interpreted asFalse, and all positiveIntegersare interpreted asTrue. Addition is logical or, multiplication is logical and.e :: Bool == True
Mod7where all arithmetic is done modulo7. SinceMod7is really just a re-interpretation ofInteger, we define a wrapper withnewtype.getMod7 (e :: Mod7) == 6