Question

I have a program that given a set of recipes and a desired output will tell me how many of the intermediate products I will need to make.

data Recipe = Recipe { name :: String
                     , ingredients :: [(Recipe, Int)]
                     } deriving (Eq)

instance Show Recipe where
        show Recipe{name=n} = show n

foldRecipes :: (Integral a) => [(Recipe, a)] -> (Recipe, a) -> [(Recipe, a)] 
foldRecipes acc r@(Recipe{ingredients=i}, n) = 
         acc ++ requirements [(recipe, fromIntegral count * n) | (recipe, count) <- i]

requirements :: (Integral a) => [(Recipe, a)] -> [(Recipe, a)] 
requirements m = 
        foldl foldRecipes m m

main = 
        let dough = Recipe{ name = "dough", ingredients = [(flour, 200), (water, 200)] }
            flour = Recipe{ name = "flour", ingredients = [] }
            water = Recipe{ name = "water", ingredients = [] }

        in putStrLn $ show $ requirements [(dough, 2)]

Outputs: [("dough",2),("flour",400),("water",400)

On my way to making this, I ran into the fact that the following version does not work, could somebody explain why? If I use an explicit Int instead of Integral a for the type signatures it does work.

foldRecipes :: (Integral a) => [(Recipe, a)] -> (Recipe, a) -> [(Recipe, a)]
foldRecipes acc r@(Recipe{ingredients=i}, _) =
         acc ++ requirements i

Output:

test_unused.hs:10:30:
    Could not deduce (a ~ Int)
    from the context (Integral a)
      bound by the type signature for
                 foldRecipes :: Integral a =>
                                [(Recipe, a)] -> (Recipe, a) -> [(Recipe, a)]
      at test_unused.hs:8:16-76
      `a' is a rigid type variable bound by
          the type signature for
            foldRecipes :: Integral a =>
                           [(Recipe, a)] -> (Recipe, a) -> [(Recipe, a)]
          at test_unused.hs:8:16
    Expected type: [(Recipe, a)]
      Actual type: [(Recipe, Int)]
    In the first argument of `requirements', namely `i'
    In the second argument of `(++)', namely `requirements i'
    In the expression: acc ++ requirements i
Was it helpful?

Solution

foldRecipes :: (Integral a) => [(Recipe, a)] -> (Recipe, a) -> [(Recipe, a)]
foldRecipes acc r@(Recipe{ingredients=i}, _) =
     acc ++ requirements i

The above does not work because ingredients always contains a list of type [(Recipe, Int)] whereas your function accepts lists that use a generic integral type Integral a. a and Int are different types and you cannot concatenate lists of different types using ++.

Your other version of the function works because there you use fromIntegral to convert from Int to the more generic type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top