Homework 4
Instructions
The source code of this homework can be found here. You should fill in the definitions of the required functions but do not change the types of the functions.
How to submit: Submit this file via the submit server.
> {-# LANGUAGE DeriveFoldable #-}
> module HW4 where
> import Test.QuickCheck
> import Prelude hiding (length, map, foldr, fold1, zipWith, concat, replicate)
> {-@ LIQUID "--no-termination" @-}
> {-@ LIQUID "--no-totality" @-}
> {-@ LIQUID "--prune-unsorted" @-}
> {-@ type True = {v:Bool | v} @-}Problem 1: Monadic Lambda Evaluation
Given the data type of expressions
> data Exp a = EVar a | EVal Int | EAdd (Exp a) (Exp a)you are asked to define its monadic instance.
- Functors: Define the
Functorinstance ofExp.
> instance Functor Exp where
> -- fmap :: (a -> b) -> Exp a -> Exp b
> fmap f (EVar x) = undefined "Define me!"
> fmap f (EVal n) = undefined "Define me!"
> fmap f (EAdd x y) = undefined "Define me!"- Applicatives: Define the
Applicativeinstance ofExp.
> instance Applicative Exp where
> -- pure :: a -> Exp a
> pure x = undefined "Define me!"
>
> -- (<*>) :: Exp (a -> b) -> Exp a -> Exp b
> ef <*> e = undefined "Define me!"- Monads: Define the
Monadinstance ofExp.
> instance Monad Exp where
> -- return :: a -> Expr a
> return x = undefined "Define me!"
>
> -- (>>=) :: Exp a -> (a -> Exp b) -> Exp b
> (EVar x) >>= f = undefined "Define me!"
> (EVal n) >>= f = undefined "Define me!"
> (EAdd x y) >>= f = undefined "Define me!"- Optional What does the
(>>=)operator for this type do?
Problem 2: Quick Check List Properties
We define up our own List data type from scratch:
> data List a = Nil
> | C a (List a)
> deriving (Eq, Ord, Show, Foldable)and it’s length function
> {-@ measure length @-}
> {-@ length :: x:List a -> Nat @-}
> length :: List a -> Int
> length Nil = 0
> length (C _ xs) = 1 + length xs- Define an arbitrary instance for the
Listdata type
> instance Arbitrary a => Arbitrary (List a) where
> arbitrary = undefined "Define me!"Once you define it, should should be able to quickcheck that adding an element to the list increases the length by 1.
> prop_cons :: a -> List a -> Bool
> prop_cons x xs = length (C x xs) == length xs + 1That is
ghci> quickCheck prop_cons
+++ OK, passed 100 tests.- Define list concatenation:
> concat :: List (List a) -> List a
> concat xss = undefined "Define me!"So that you can quickeck the following property:
> prop_concat :: List (List a) -> Bool
> prop_concat xss = length (concat xss) == lengths xsswhere lengths computes the sum of lengths of a list of lists
> {-@ measure lengths @-}
> lengths :: List (List a) -> Int
> lengths Nil = 0
> lengths (C x xs) = length x + lengths xs - Define the function
replicate i xthat returns a list that contains the valuex,itimes
> replicate :: Int -> a -> List a
> replicate i a = undefined "Define me!"When you are done, the following property should quickcheck.
> {-@ prop_replicate :: Nat -> a -> Property @-}
> prop_replicate :: Int -> a -> Property
> prop_replicate n x = 0 <= n ==> n == length (replicate n x)Problem 3: Verify List Properties
We used Quickcheck to gain high confidence on the correctness of our definitions but we already observed one limitation: replicate behaves as expected but only on non-negative inputs. What about negative ones?
- Partial Functions:
replicate
We can use a Liquid type for replicate that specifies that replicate is only defined on i that is a natural number.
> {-@ replicate :: i:Nat -> a -> {v:List a | true } @-}But what about its result type? Edit the result type of replicate in the above type, so the following is accepted by Liquid Haskell.
> {-@ prop_replicate_lh :: Nat -> a -> True @-}
> prop_replicate_lh :: Int -> a -> Bool
> prop_replicate_lh n x = n == length (replicate n x)- Higher Order Functions: With Liquid Haskell you can also easily check properties of higher order functions.
map
> {-@ map :: (a -> b) -> xs:List a -> {v:List b | true } @-}
> map f Nil = Nil
> map f (C x xs) = f x `C` map f xsSo that the below length preserving property is accepted by Liquid Haskell
> {-@ prop_map :: (a -> b) -> List a -> True @-}
> prop_map :: (a -> b) -> List a -> Bool
> prop_map f xs = length xs == length (map f xs)- Higher Order, Partial Functions:
Finally, fix the specification for foldr1 so that the call to error is verified by Liquid Haskell, as dead code:
> {-@ foldr1 :: (a -> a -> a) -> {v:List a | true } -> a @-}
> foldr1 op (C x xs) = foldr op x xs
> foldr1 op Nil = error "Cannot call foldr1 with empty list"
>
> foldr :: (a -> b -> b) -> b -> List a -> b
> foldr _ b Nil = b
> foldr op b (C x xs) = x `op` (foldr op b xs)- Verification of checked properties.
You can use Liquid Haskell to check all the properties of Problem 2. For the homework, let’s just check prop_concat:
concat:
> {-@ concat :: xs:List (List a) -> {v:List a | true } @-}So that the length preserving property is checked by Liquid Haskell
> {-@ prop_concat :: List (List a) -> True @-}