I have the following definitions:
data Egg = ChickenEgg | ChocolateEgg
deriving Show
data Milk = Milk Int -- amount in litres
deriving Show
class Price a where
price :: a -> Int
instance Price Egg where
price ChickenEgg = 20
price ChocolateEgg = 30
instance Price Milk where
price (Milk v) = 15 * v
The above compiles and runs OK. However, I need to be able to do something like that:
price (Just ChickenEgg) -- 20
price [Milk 1, Milk 2] -- 45
price [Just ChocolateEgg, Nothing, Just ChickenEgg] -- 50
price [Nothing, Nothing, Just (Milk 1), Just (Milk 2)] -- 45
My best attempt for price (Just ChickenEgg)
was:
instance Price (Maybe Egg) where
price Nothing = 0
price (Just ChickenEgg) = 20
price (Just ChocolateEgg) = 30
Q: What am I doing wrong here? :/
If this is really what you want to do, you can write an instance like this:
instance Price a => Price (Maybe a) where
price Nothing = 0
price (Just x) = price x
This enables not only price (Just ChickenEgg)
, but getting the price of any Maybe Price
instance:
ghci> price (Just ChickenEgg)
20
ghci> price (Just (Milk 2))
30
This, however, doesn't feel quite like idiomatic Haskell to me. There are certainly use cases where something like this is useful (e.g. QuickCheck does this a lot), but often it's safer to keep the Maybe
or list structure intact, and instead rely on built-in combinators:
ghci> price <$> Just ChickenEgg
Just 20
You can do the same with your other examples, without adding more type class instances:
ghci> sum $ price <$> [Milk 1, Milk 2]
45
ghci> sum $ mapMaybe (fmap price) [Just ChocolateEgg, Nothing, Just ChickenEgg]
50
ghci> sum $ mapMaybe (fmap price) [Nothing, Nothing, Just (Milk 1), Just (Milk 2)]
45