Real-Time Queues

RealTimeQueue.elm

Combine reverse and append into an incremental function that performs one step of each at a time:

rotate : LazyList a -> List a -> LazyList a -> LazyList a
rotate xs ys acc =
  case (force xs, ys) of
    (Nil        , y::[])  -> lazy (\_ -> Cons y acc)
    (Cons x xs_ , y::ys_) ->
      lazy (\_ -> Cons x (rotate xs_ ys_ (lazy (\_ -> Cons y acc))))

Starting with the BankersQueue, use an ordinary List to describe the back, and track a “schedule” LazyList a that is a suffix of front that needs to be forced. The implementation enforces the invariant that the size of sched is equal to the size of front minus the size of back. This invariant obviates the need to maintain Int size information explicitly.

type Queue a = Q (LazyList a) (List a) (LazyList a)

The basic operations:

empty : Queue a
empty = Q (lazy (\_ -> Nil)) [] (lazy (\_ -> Nil))

isEmpty : Queue a -> Bool
isEmpty (Q front _ _) =
  case force front of
    Nil -> True
    _   -> False

peek : Queue a -> Maybe a
peek (Q front _ _) = maybeHead front

Because of the invariant, sched is empty when the lengths of front and back are the same. So when enqueueing an element, the rotation from back to front is initiated when sched is empty.

enqueue : a -> Queue a -> Queue a
enqueue x (Q front back sched) =
  case force sched of
    Cons _ sched_ -> Q front (x::back) sched_
    Nil ->
      let front_ = rotate front (x::back) (lazy (\_ -> Nil)) in
      Q front_ [] front_

Similarly, dequeue initiates the rotation when the back becomes longer than the front.

dequeue : Queue a -> Maybe (Queue a)
dequeue (Q front back sched) =
  case force front of
    Nil -> Nothing
    Cons _ front_ ->
      case force sched of
        Cons _ sched_ -> Just (Q front_ back sched_)
        Nil ->
          let front__ = rotate front_ back (lazy (\_ -> Nil)) in
          Just (Q front__ [] front__)

The common parts of these operations can be factored out into an exec function.

enqueue : a -> Queue a -> Queue a
enqueue x (Q front back sched) = exec front (x::back) sched

dequeue : Queue a -> Maybe (Queue a)
dequeue (Q front back sched) =
  case force front of
    Nil       -> Nothing
    Cons _ f_ -> Just (exec f_ back sched)

exec front back sched =
  case force sched of
    Cons _ sched_ -> Q front back sched_
    Nil ->
      let front_ = rotate front back (lazy (\_ -> Nil)) in
      Q front_ [] front_


Reading

Optional