1
votes

Alright so I have two types:

data Ingredient = Ingredient { potato :: String, amount :: Int, cookingTime :: Int }

data Soup = Soup { availableTime :: Int, recipe :: [Ingredient], shoppingList :: [Ingredient], canBeDone :: Bool  }

now if I want to compare the "available Time" and the "cookingTime" of an Ingredient (basically if I have enough time to cook that ingredient if I only have "availableTime" to cook the whole soup). And if I can cook the Ingredient in my soup, the Ingredient gets shifted into the shoppingList list. How do I go about that? Here's what I came up with:

doIHaveTime :: Soup -> Soup
doIHaveTime Soup{availableTime = a, recipe = [Ingredient{cookingTime = b}] } = if a >= b then Soup{ shoppingList = b:xs } else show "Can't be done."

does that way of thinking make sense? These are the errors I get:

soupExample.hs:6:128: error: • Couldn't match type ‘[Char]’ with ‘Soup’ Expected type: Soup Actual type: String • In the expression: show "Can't be done." In the expression: if a >= b then Soup {shoppingList = b : xs} else show "Can't be done." In an equation for ‘doIHaveTime’: doIHaveTime (Soup {availableTime = a, recipe = [Ingredient {cookingTime = b}]}) = if a >= b then Soup {shoppingList = b : xs} else show "Can't be done." Failed, modules loaded: none.

1
recipe = [Ingredient{cookingTime = b}] will only handle recipes with one ingredient, and crash on the other cases. You can use recipe = is to bind the whole list, or do two cases, recipe = [] and recipe = Ingredient{cookingTime = b} : is. - chi
Also, the type Soup -> Soup looks wrong, since you are not returning a soup if there's not enough time. Maybe you want Soup -> Maybe Soup ? - chi
shoppingList should probably not be part of Soup; that should be a list of Ingredients independent of whatever Soup requires the ingredients. - chepner
I should probably mention that record field access is one of Haskell's weakest points. There are some partial solutions (notably optics) that you'll probably want to learn about once you get some more experience. - dfeuer

1 Answers

1
votes

To handle multiple ingredients in a soup you can use all :: (a -> Bool) -> [a] -> Bool to check if every ingredient in the recipe can be cooked in the available time:

enoughTime = all ((>=available) . cookingTime) ingredients

Then you have to return a Soup in both if and else branches. In total this can look like this:

doIHaveTime :: Soup -> Soup
doIHaveTime soup@Soup{ availableTime = available, recipe = ingredients }
    = if enoughTime
        -- add ingredients to shoppingList and set canBeDone to True
        then soup { shoppingList = ingredients
                  , canBeDone = True }
        -- clear shoppingList and set canBeDone to False
        else soup { shoppingList = []
                  , canBeDone = False }
  where enoughTime = all ((>=available) . cookingTime) ingredients

Note that I used soup@Soup{ ... } to pattern match and store the value at the same time in the variable soup. That way you only have to modify the two fields shoppingList and canBeDone.