This is my first attempt at creating a custom instance of a class such as Ord.
I've defined a new data structure to represent a list:
data List a = Empty | Cons a (List a)
deriving (Show, Eq)
Now I want to define a new instance of Ord for List such that List a <= List b implies "the sum of the elements in List a is less than or equal to the sum of the elements in List b"
first of all, is it necessary to define a new "sum" function since the sum defined in Prelude wont work with the new List data type? then, how do I define the new instance of Ord for List?
thanks
First of all, this won't work exactly like the normal list instance. The normal instance only depends on the items of the list being orderable themselves; your proposal depends on their being numbers (eg in the Num
class) and so is more narrow.
It is necessary to define a new sum
function. Happily it's very easy to write sum
as a simple recursive function. (Coincidentally, you can call your function sum'
, which is pronounced as "sum prime" and by convention means it's a function very similar to sum
.)
Additionally, the instance would have to depend on the Num
class as well as the Ord
class.
Once you have a new sum
function, you can define an instance something like this:
instance (Ord n, Num n) => Ord (List n) where compare = ...
-- The definition uses sum'
This instance statement can be read as saying that for all types n
, if n
is in Ord
and Num
, List n
is in Ord
where comparisons work as follows. The syntax is very similar to math where =>
is implication. Hopefully this makes remembering the syntax easier.
You have to give a reasonable definition of compare
. For reference, compare ab
works as follows: if a < b
it returns LT
, if a = b
it returns EQ
and if a > b
it returns GT
.
This is an easy function to implement, so I'll leave it as an exercise to the reader. (I've always wanted to say that :P).
How about...
newtype List a = List [a]
This is very common if you want to introduce new, "incompatible" type class instances for a given type (see eg ZipList
or several monoids like Sum
and Product
)
Now you can easily reuse the instances for list, and you can use sum
as well.
Generalizing @Tikhon's approach a bit, you could also use Monoid
instead of Num
as constraint, where you already have a predefined "sum" with mconcat
(of course, you still needed Ord
). This would give you some more types into consideration than just numbers (eg List (List a)
, which you can now easily define recursively)
On the other hand, if you do want to use a Num
as monoid, you have to decide every time for Sum
or Product
. It could be argued, that having to write this out explicitly can reduce the shortness and readability, but it's a design choice that depends on what degree of generality you want to have in the end.
What about ..
data List a = Empty | Cons a (List a)
deriving (Show, Eq)
instance (Ord a, Num a, Eq a) => Ord (List a) where
-- 2 empty lists
Empty <= Empty = True
-- 1 empty list and 1 non-empty list
Cons a b <= Empty = False
Empty <= Cons a b = True
-- 2 non-empty lists
Cons a b <= Cons c d = sumList (Cons a b) <= sumList (Cons c d)
-- sum all numbers in list
sumList :: (Num a) => List a -> a
sumList Empty = 0
sumList (Cons n rest) = n + sumList rest
Is this what you are looking for?
.. or another solution with the sum function in Prelude.
data List a = Empty | Cons a (List a)
deriving (Show, Eq)
instance (Ord a, Num a, Eq a) => Ord (List a) where
-- 2 empty lists
Empty <= Empty = True
-- 1 empty list and 1 non-empty list
Cons a b <= Empty = False
Empty <= Cons a b = True
-- 2 non-empty lists
Cons a b <= Cons c d = sum (listToList (Cons a b))
<= sum (listToList (Cons c d))
-- convert new List to old one
listToList :: (Num a) => List a -> [a]
listToList Empty = []
listToList (Cons a rest) = [a] ++ listToList rest
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.