""
{-
data Currency = USD | EUR deriving Show
-- We use `Currency` values to create `Amount` types
-- read about types in Haskell ([Kinds][1]: *, * -> *, ...)
-- here we fix one * to be Currency
data Amount :: Currency -> * where
-- Data constructor, take one float and return some Amount
Amount :: Float -> Amount a
-- Extract the specific currency symbol require extra effort
instance Show (Amount a) where
show (Amount k) = show k
-- Many amounts (same currency)
-- `a` restrict `a1` and `a1` to have the same type => the same currency
data PData a = PData { a1 :: Amount a
, a2 :: Amount a
} deriving Show
-- Helpers
usd :: Float -> Amount USD
usd = Amount
eur :: Float -> Amount EUR
eur = Amount
main = do
print $ PData (usd 3) (usd 4) -- OK
print $ PData (eur 3) (eur 4) -- OK
print $ PData (eur 3) (usd 4) -- KO, Couldn't match type 'USD with 'EUR
(1) https://wiki.haskell.org/Kind
, @TheInnerLight , phantom
-- NOTE: this is not a "direct translation" since currencies are not
-- enumerated and is slightly different
data USD = USD
data EUR = EUR
data Amount c = Amount { amount :: Float }
instance Show (Amount c) where
show (Amount a) = show a
data PData c = PData { c1 :: Amount c
, c2 :: Amount c }
deriving Show
usd :: Float -> Amount USD
usd = Amount
eur :: Float -> Amount EUR
eur = Amount
main = do
print $ PData (usd 3) (usd 4) -- OK
print $ PData (eur 3) (eur 4) -- OK
print $ PData (eur 3) (usd 4) -- KO, Couldn't match type 'USD with 'EUR
( )
class Symbol c where symbol :: c -> String
instance Symbol USD where symbol _ = "USD"
instance Symbol EUR where symbol _ = "EUR"
instance Symbol c => Show (Amount c) where
show s@(Amount a) = sym undefined s ++ " " ++ show a
where sym :: Symbol c => c -> Amount c -> String
sym k _ = symbol k
PData {c1 = USD 3.0, c2 = USD 4.0}
PData {c1 = EUR 3.0, c2 = EUR 4.0}