Reputation: 333
Im attempting to find the max element of a list, where the elements are a data type created by myself, using a fold instead of doing it recursively. However i end up with an error "couldn't match type". As im fairly new to haskell im having trouble seeing the problem and wish to know how to correctly apply the foldr function.
My attempt at obtaining the largest elements looks like this:
-- (foldr comparison (head list) (tail list))
which won't compile.
i have included the datatype and its instances for Eq and Ord, and i've also included the comparison function.
-- Car data type
data Car = Car {registration :: String,
hour :: Integer,
minute :: Integer,
tupleForm :: PTime
}
-- Equality comparison between Car data types
-- reqiures equality by registration number, hours and minutes
instance Eq Car where
(Car r1 h1 m1 _) == (Car r2 h2 m2 _) = (r1 == r2) && (((h1*60)+m1) ==
((h2*60)+m2))
-- Order comparison between Car data types
-- Compares time of two Cars , hours and minutes
instance Ord Car where
compare (Car _ h1 m1 _) (Car _ h2 m2 _) = compare ((h1*60)+m1)
((h2*60)+m2)
-- Returns the larger Car
comparison :: (Car,Car) -> Car
comparison(car1,car2) = if(car1 > car2) then car1 else car2
My expected result after folding the list of Car is to obtain the 'largest car' ,which basically means the car with the largest time. But I end up with a compilation error due to faulty type.
Upvotes: 1
Views: 860
Reputation: 139611
Consider the simplified type of foldr
.
foldr :: (a -> b -> b) -> b -> [a] -> b
This is a lot more general than you require because you’re dealing with all Cars. That means to find the biggest Car
with foldr
, the type becomes
foldr :: (Car -> Car -> Car) -> Car -> [Car] -> Car
The first argument is a function that selects between two Cars. In your case, you want max
because its type
max :: Ord a => a -> a -> a
becomes
max :: Car -> Car -> Car
and matches exactly.
The second argument to foldr
is named z
for zero. It is the seed for the folding process. For this, you may as well use the first element of your list, obtained with head
.
The list argument of type [Car]
is obviously the list whose maximum you want to compute. You could pass the entire list, but the head is already accounted for as the z
argument. Better would be tail list
.
Given the following list (after modifying Car
to remove tupleForm
and derive a Show
instance)
cars = [ Car "A" 1 2, Car "B" 3 4, Car "C" 10 10 ]
finding the maximum with foldr
is
λ> foldr max (head cars) (tail cars)
Car {registration = "C", hour = 10, minute = 10}
Note that this application of foldr
is equivalent to maximum
, but you don’t have to take my word for it. Adding
import Test.QuickCheck
to the top of your source file and then
prop_max :: [Car] -> Property
prop_max l =
not (null l) ==>
maximum l == foldr max (head l) (tail l)
instance Arbitrary Car where
arbitrary = do
r <- oneof $ map return ["Apple","Orange","Banana"]
h <- choose (0,23)
m <- choose (0,59)
return (Car r h m)
gives more confidence in the assertion.
λ> quickCheck prop_max
+++ OK, passed 100 tests.
Upvotes: 1
Reputation: 531808
The problem is the type and definition of comparison
.
First, the type should be Car -> Car -> Car
: you take two Car
values and return the bigger one.
Second, your definition of comparison
tries to match a single argument as a tuple, rather than two separate arguments. Drop the parentheses and the comma.
comparison :: Car -> Car -> Car
comparison car1 car2 = if car1 > car2 then car1 else car2
(Of course, comparison
is just max
restricted to Car
rather than Ord a => a
.
comparison :: Car -> Car -> Car
comparison = max
And, as Robin Zigmond points out, foldr comparison x
is basically maximum
for an appropriate value x
.)
Upvotes: 3