Окасаки описывает постоянные очереди в реальном времени, которые могут быть реализованы в Haskell с использованием типа
data Queue a = forall x . Queue
{ front :: [a]
, rear :: [a]
, schedule :: [x]
}
где инкрементные вращения поддерживают инвариант
length schedule = length front - length rear
Подробнее
Если вы знакомы с задействованными очередями, вы можете пропустить этот раздел.
Функция вращения выглядит как
rotate :: [a] -> [a] -> [a] -> [a]
rotate [] (y : _) a = y : a
rotate (x : xs) (y : ys) a =
x : rotate xs ys (y : a)
и он вызывается интеллектуальным конструктором
exec :: [a] -> [a] -> [x] -> Queue a
exec f r (_ : s) = Queue f r s
exec f r [] = Queue f' [] f' where
f' = rotate f r []
после каждой операции очереди. Интеллектуальный конструктор всегда вызывается, когда length s = length f - length r + 1
, гарантируя, что соответствие шаблона в rotate
будет успешным.
Проблема
Я ненавижу частичные функции! Мне бы хотелось найти способ выразить структурный инвариант в типах. Обычный зависимый вектор кажется вероятным выбором:
data Nat = Z | S Nat
data Vec n a where
Nil :: Vec 'Z a
Cons :: a -> Vec n a -> Vec ( n) a
а затем (возможно)
data Queue a = forall x rl sl . Queue
{ front :: Vec (sl :+ rl) a
, rear :: Vec rl a
, schedule :: Vec sl x
}
Проблема в том, что я не смог понять, как манипулировать типами. Кажется весьма вероятным, что для достижения этой эффективности потребуется некоторое количество unsafeCoerce
. Тем не менее, я не смог придумать подход, который даже смутно управляем. Возможно ли это сделать в Haskell?