Now I have another problem:
Another subtask tells me to derive a Currency data type from the Eq and Ord classes.
The problem is again that I'm not allowed to use deriving (Ord, Eq).
I don't know how to compare each currency. Do you have an idea and could you explain it to me so that I can understand?
data Currency = Dollar Integer Integer | Yen Integer | Euro Integer Integer
instance Eq Currency where
...
instance Ord Currency where
...
CodePudding user response:
In general, the way to approach this kind of problem is to not get over-distracted by the type class machinery. I suggest you start by writing a function like eqCurrency
:
eqCurrency :: Currency -> Currency -> Bool
eqCurrency ...
The obvious next step is to put in your arguments and do some pattern matching. For example, I'd probably write:
eqCurrency :: Currency -> Currency -> Bool
eqCurrency (Dollar d1 c1) (Dollar d2 c2) = ...
eqCurrency (Yen y1) (Yen y2) = ...
eqCurrency (Euro d1 c1) (Euro d2 c2) = ...
Next, you have to decide how to check equality for different types of currency. So, ask yourself, is there ever a case where some dollar amount can equal some yen amount? If so, write cases for that. If not, perhaps you can just add a catch-all pattern match like
eqCurrency _c1 _c2 = False
Once you've written this function and are happy with its behavior, you can slot it in to the type class:
instance Eq Currency where
(==) = eqCurrency
You'll need to do the same thing for the Ord
type class. For Ord
, the minimal complete definition is either defining <=
or compare
, so you should choose one of those. For instance, you could define:
leqCurrency :: Currency -> Currency -> Bool
leqCurrency ...
Once again, you'll ask yourself the same questions: If I have two Dollar
amounts, when is one less than the other? If I have a Euro
and a Yen
, which one is less than the other? I can't answer these semantic questions for you, but you'll use your answers to encode the various cases, and when you're finished, you can fill in the type class:
instance Ord Currency where
(<=) = leqCurrency
CodePudding user response:
I don't know how to compare each currency
Me neither. "How should currencies be compared" is more of a question of design than of implementation.
If you want to go for full correctness, you'd have to use the internet to download current exchange rates in order to do the comparison. But Eq
and Ord
instances simply don't allow internet connection, so that's not really possible.
It sounds like this is for an exercise, so perhaps it'd be fair to choose some fixed exchange rates for your program? Google tells me that, at the time of writing, one US cent is worth 1.4 Yen, so I'll go with that.
Then we know one case for ord:
instance Ord Currency where
compare (Dollar dollars cents) (Yen yen) = compare ((dollars*100 cents) * 1.4) yen
-- ...
The rest of the Ord
instance could be filled in in a similar fashion, with cases for compare (Yen ...) (Dollar ...)
and compare (Dollar ...) (Euro ...)
etc, etc.
That implementation will work, and would be worth filling out completely.
There is a "cleaner" solution, though, if you're interested. We can choose some "anchor" currency, say Yen. Then we can write a so-called 'normalization' function which converts every currency to Yen:
normalize :: Currency -> Integer
normalize (Dollar dollars cents) = (dollars*100 cents) * 1.4
-- you fill in the rest
Using this, we can write Ord
more simply as just
instance Ord Currency where
compare c1 c2 = compare (normalize c1) (normalize c2)