Haskell: положить в государственную монаду, кажется, исключено

Я пишу программу для раздачи пиццы людям; каждый человек получит одну пиццу, в идеале своего любимого типа, если на складе не закончится, и в этом случае ему дадут рекурсивный следующий тип.

Мой подход состоит в том, чтобы вычислить ((User, Pizza), Int) на сумму, которую человек хотел бы сказать пиццу, отсортировать ее и обработать с помощью государственной монады, чтобы вести учет инвентаря.

Программа написана и проверяет тип:

allocatePizzasImpl :: [((User, Pizza), Int)] 
                   -> State [(Pizza, Int)] [(User, Pizza)]
allocatePizzasImpl [] = return []
allocatePizzasImpl ((user, (flavor, _)):ranks) =
    do inventory <- get
       -- this line is never hit
       put $ updateWith inventory (\i -> if i <= 0
                                         then Nothing
                                         else Just $ i - 1) flavor
       next <- allocatePizzasImpl $ filter ((/= user) . fst) ranks
       return $ (user, flavor) : next

и у меня есть вспомогательная функция для извлечения результата:

allocatePizzas :: [Pizza] 
               -> [((User, Pizza), Int)] 
               -> [(User, Pizza)]
allocatePizzas pizzas rank = fst 
                           . runState (allocatePizzasImpl rank) 
                           $ buildQuotas pizzas

но линия обозначена -- this line is never hit... никогда не попадает в точки останова GHCI; Более того, если я прерву ответный звонок, GHCI говорит inventory не в сфере.

При запуске результатом является присвоение одной и той же пиццы (с одним инвентарным счетом) всем пользователям. Что-то идет не так, но я абсолютно не знаю, как поступить. Я новичок в Haskell, поэтому любые комментарии по стилю также приветствуются =)

Спасибо!

PS: для полноты updateWith определяется как:

updateWith :: (Eq a, Eq b) 
           => [(a, b)]        -- inventory
           -> (b -> Maybe b)  -- update function; Nothing removes it
           -> a               -- key to update
           -> [(a, b)]
updateWith set update key =
    case lookup key set of
      Just b -> replace set
                        (unwrapPair (key, update b))
                        (fromMaybe 0 $ elemIndex (key, b) set)
      Nothing -> set
  where replace :: [a] -> Maybe a -> Int -> [a]
        replace [] _ _ = []
        replace (_:xs) (Just val) 0 = val:xs
        replace (_:xs) Nothing 0 = xs
        replace (x:xs) val i = x : (replace xs val $ i - 1)

        unwrapPair :: Monad m => (a, m b) -> m (a, b)
        unwrapPair (a, mb) = do b <- mb
                                return (a, b)

2 ответа

Решение

Я думаю твоя функция replace сломано:

replace (_:xs) (Just val) 0 = val:xs

Это не обращает никакого внимания на значение, которое он заменяет. Не было ли у вас намерения заменить только пару, соответствующую key?

Я думаю ты хочешь

updateWith [] e k = []
updateWith ((k', v):kvs) e k
    | k' == k = case e v of
        Just v' -> (k, v'):kvs
        Nothing -> kvs
    | otherwise = (k', v) : updateWith kvs e k

Проблема (игнорирование других концептуальных вещей, упомянутых комментаторами) оказалась в использовании fst получение результата от государства по некоторым причинам не приведет к тому, что государство будет фактически вычислено. Выполнение результата через seq починил это.

Мне было бы интересно узнать, почему это так!

Редактировать: Как отметил Даниэль Вагнер в комментариях, я фактически не использовал инвентарь, который оказался настоящей ошибкой. Отметить это как принятый.

Другие вопросы по тегам