CIS 552: Advanced Programming

Fall 2017

  • Home
  • Schedule
  • Homework
  • Resources
  • Style guide
  • Syllabus
Note: this is the stubbed version of module MonoidFoldable. You should download the lhs version of this module and replace all parts marked undefined. Eventually, the complete version will be made available.

Exercise: Monoid and Foldable

> {-# LANGUAGE DeriveFunctor #-}
> {-# LANGUAGE FlexibleInstances #-}
> {-# OPTIONS_GHC -fdefer-type-errors #-}
> module MonoidFoldable where
> import Prelude hiding (all,any, and, or)
> import Data.Foldable hiding (and, or, any, all)
> import Data.Monoid hiding (All, getAll, Any, getAny)
> import Test.HUnit

Monoids

First, read just the 'Monoids' section of HW 03's SortedList module.

Note that this section defines the following function that tailors a fold operation to a specific instance of the Monoid class.

> reduce :: Monoid b => [b] -> b
> reduce = foldr mappend mempty

For example, because the String type is an instance of this class (using ++ for mappend) we can reduce a list of Strings to a single string.

> tm0 :: Test
> tm0 = reduce ["C", "I", "S", "5", "5", "2" ] ~?= "CIS552"

The assignment shows you that numbers can instantiate this class in multiple ways. Like numbers, Booleans can be made an instance of the Monoid class in two different ways.

> newtype All = All { getAll :: Bool }
> newtype Any = Any { getAny :: Bool }

Make sure that you understand these type definitions. We are defining a type All with single data constructor (also called All). The argument of this data constructor is a record with a single field, called getAll. What this means is that All and getAll allow us to convert Bools to All and back.

  λ> :t All
  All :: Bool -> All
  λ> :t getAll
  getAll :: All -> Bool

Above, newtype is like data, but is restricted to a single variant. It is typically used to create a new name for an existing type. This new name allows us to have multiple instances for the same type (as below) or to provide type abstraction (like SortedList in the HW).

Your job is to complete these instances that can tell us whether any of the booleans in a list are true, or whether all of the booleans in a list are true. (See two test cases below for an example of the behavior.)

> instance Monoid All where
>   mempty  = undefined
>   mappend = undefined
> instance Monoid Any where
>   mempty  = undefined
>   mappend = undefined
> tm1 :: Test
> tm1 = getAny (reduce (map Any [True, False, True])) ~?= True
> tm2 :: Test
> tm2 = getAll (reduce (map All [True, False, True])) ~?= False

Foldable

Now, read the section marked The Foldable Typeclass in the MergeSort module.

We can use your Monoid instances for Any and All to generalize operations to any data structure.

For example, we can generalize the and operation to any Foldable data structure using foldMap.

> and :: Foldable t => t Bool -> Bool
> and = getAll . foldMap All

Your job is to define these three related operations

> or :: Foldable t => t Bool -> Bool
> or = undefined
> all :: Foldable t => (a -> Bool) -> t a -> Bool
> all f = undefined
> any :: Foldable t => (a -> Bool) -> t a -> Bool
> any f = undefined

so that the following tests pass

> tf0 :: Test
> tf0 = or [True, False] ~?= True
> tf1 :: Test
> tf1 = all (>0) [1::Int,2,4,18] ~?= True
> tf2 :: Test
> tf2 = all (>0) [1::Int,-2,4,18] ~?= False
> tf3 :: Test
> tf3 = any (>0) [1::Int,2,4,18] ~?= True
> tf4 :: Test
> tf4 = any (>0) [-1::Int,-2,-4,-18] ~?= False

Application

Recall our familiar Tree type. Haskell can derive the Functor instance for this type so we ask it to do so.

> data Tree a = Empty | Branch a (Tree a) (Tree a) deriving (Eq, Functor)

And here is an example Tree.

> t1 :: Tree String
> t1 = Branch "d" (Branch "b" (l "a" ) (l "c")) (Branch "f" (l "e") (l "g")) where
>        l x = Branch x Empty Empty

We could make this type an instance of Foldable using the definition of foldrTree from hw02.

But, for practice, complete the instance using foldMap.

> instance Foldable Tree where
>   foldMap = undefined

With this instance, we can for example, verify that all of the sample strings above have length 1.

> tt1 :: Test
> tt1 = all ((== 1) . length) t1 ~?= True

Finally, look at the documentation for the Foldable class and find some other tree operations that we get automatically for free.

> tt2 :: Test
> tt2 = undefined

Oblig-main

> main = runTestTT $ TestList [tm0, tm1, tm2, tf0, tf1,tf2,tf3,tf4, tt1]
Design adapted from Minimalistic Design | Powered by Pandoc and Hakyll