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
Functor
instance 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
Applicative
instance 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
Monad
instance 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
List
data 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 + 1
That 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 xss
where 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 x
that returns a list that contains the valuex
,i
times
> 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 xs
So 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 @-}