Lets assume these 2 sum types
data Currency =
| GBP
| EUR
| DKK
data Country =
| DE
| AT
| DK
| UK
and the following product type
type CC = (Country, Currency)
Now even though all these countries are part of the European Union (yeah, dear software archeologist from the year 3000 - the UK was part of the EU once ;-) ) they have different currencies (or not). So I'd like to restrict the possible values of CC to
(DE, EUR)
(AT, EUR)
(UK, GBP)
(DK, DKK)
and make every other combination not expressable.
Is it possible to express such a thing on the type level?
If not how would a well versed Haskeller approach this otherwise?
This may be overkill, but depending on the context you are in, you could use a GADT. This is hinging a lot on the fact your at least your currencies don't have any constructor information.
{-# LANGUAGE GADTs, DataKinds #-}
data Currency = GBP | EUR | DKK
data Country c where
DE :: Country EUR
AT :: Country EUR
UK :: Country GBP
DK :: Country DKK
Or, a variant I think is probably less useful but closer to the question
{-# LANGUAGE GADTs, DataKinds #-}
data Currency = GBP | EUR | DKK
data Country = DE | AT | DK | UK
data CountryCurrency country currency where
DECC :: CountryCurrency DE EUR
ATCC :: CountryCurrency AT EUR
UKCC :: CountryCurrency UK GBP
DKCC :: CountryCurrency DK DKK
Without a use case, it is tough to say what the best approach is. :)
My contention is that a product type is the wrong way to represent this mapping, and that types are not a good tool for checking its consistency. (How do you make sure you've got the mapping in the type right? If the mapping changes then you need to change the type too.)
Each country has exactly one currency, and the currency is uniquely determined by the country. Sounds more like a function than a pair.
currency :: Country -> Currency
currency DE = EUR
currency AT = EUR
currency UK = GBP
currency DK = DKK
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With