An immutable queue library in F#. The queue is divided into two lists : one for dequeueing, and one for enqueueing. All operations are based on the List module.

There are a few exceptions however : operations involving arithmetic (such as List.sum) are not included.

Source

// Adapted from F# ListEnumerator from fslib/prim-types.fs

type private QueueEnumerator<'a>(enqueued : 'a list, toDequeue :'a list) =
  static let notStarted() =
    raise (new System.InvalidOperationException("Enumeration has not started. Call MoveNext."))

  static let alreadyFinished() =
    raise (new System.InvalidOperationException("Enumeration already finished."))

  static let nonempty x = not <| List.isEmpty x 

  let mutable backData = enqueued
  let mutable frontData = toDequeue
  let moveBackToFront() =
    match frontData, backData with
    | [], hd::tl -> frontData <- List.rev backData
    | _ -> ()

  let mutable started = false

  member x.GetCurrent() =
    if started then
      moveBackToFront()
      match frontData with
      | hd :: tl -> hd
      | [] -> alreadyFinished()
    else
      notStarted()

  interface System.Collections.Generic.IEnumerator<'a> with
    member x.Current = x.GetCurrent()

  interface System.Collections.IEnumerator with
    member x.MoveNext() =
      moveBackToFront()
      if started then
        match frontData with
        | hd::tl ->
            frontData <- tl
            nonempty frontData
        | [] -> false
      else
        started <- true
        nonempty frontData

    member x.Current = box (x.GetCurrent())

    member x.Reset() =
      started <- false
      backData <- enqueued
      frontData <- toDequeue

  interface System.IDisposable with
    member x.Dispose() = ()  

type Queue<'a> =
  { enqueued : 'a list
    toDequeue : 'a list
  }
  interface System.Collections.Generic.IEnumerable<'a> with
    member x.GetEnumerator() =
      (new QueueEnumerator<'a>(x.enqueued, x.toDequeue) :> System.Collections.Generic.IEnumerator<'a>)

  interface System.Collections.IEnumerable with
    member x.GetEnumerator() =
      (new QueueEnumerator<'a>(x.enqueued, x.toDequeue) :> System.Collections.IEnumerator)  

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Queue =

  type t<'a> = Queue<'a>

  let unsafe_create enqueued toDequeue =
    { enqueued = enqueued
      toDequeue = toDequeue
    }  

  let toList xs = xs.toDequeue @ List.rev xs.enqueued

  let ofList xs = unsafe_create [] xs

  [<GeneralizableValueAttribute ()>]
  let empty<'a> : Queue<'a> = unsafe_create [] []

  let rec peek q =
    match q.enqueued, q.toDequeue with
    | _, hd::tl -> hd
    | hd::tl as e, [] -> e |> List.rev |> List.head
    | _ -> invalidArg "q" "peek : empty queue"

  let rec tryPeek q =
    match q.enqueued, q.toDequeue with
    | _, hd::tl -> Some hd
    | hd::tl as e, [] -> e |> List.rev |> ofList |> tryPeek
    | _ -> None

  let enqueue x q = {q with enqueued = x :: q.enqueued}

  let rec dequeue q =
    match q.enqueued, q.toDequeue with
    | _, hd::tl -> hd, {q with toDequeue = tl}
    | hd::tl as e, [] -> e |> List.rev |> ofList |> dequeue
    | _ -> invalidArg "q" "dequeue : empty queue"

  let rec tryDequeue q =
    match q.enqueued, q.toDequeue with
    | _, hd::tl -> Some (hd, {q with toDequeue = tl})
    | hd::tl as e, [] -> e |> List.rev |> ofList |> tryDequeue
    | _ -> None

  let append xs xs' = List.append (toList xs) (toList xs') |> ofList
  let choose f xs =
    unsafe_create (List.choose f xs.enqueued) (List.choose f xs.toDequeue)
  let collect f xs =
    unsafe_create (List.collect f xs.enqueued) (List.collect f xs.toDequeue)
  let concat xss = unsafe_create [] [for xs in xss do yield! toList xs]
  let exists p xs =
    List.exists p xs.enqueued || List.exists p xs.toDequeue
  let exists2 p xs xs' = List.exists2 p (toList xs) (toList xs')
  let filter p xs =
    unsafe_create (List.filter p xs.enqueued) (List.filter p xs.toDequeue)
  let find p xs =
    match List.tryFind p xs.toDequeue with
    | None -> List.find p (List.rev xs.enqueued)
    | Some x -> x
  let findAll p xs = filter p xs
  let findIndex p xs =
    match List.tryFindIndex p xs.toDequeue with
    | None -> List.findIndex p (List.rev xs.enqueued)
    | Some x -> x
  let fold f v xs = xs |> toList |> List.fold f v
  let fold2 f v xs xs' = List.fold2 f v (toList xs) (toList xs')
  let foldBack f xs v = List.foldBack f (toList xs) v
  let foldBack2 f xs xs' v = List.foldBack2 f (toList xs) (toList xs') v
  let forAll p xs =
    List.forall p xs.enqueued && List.forall p xs.toDequeue
  let forAll2 p xs xs' =
    List.forall2 p (toList xs) (toList xs')
  let head xs = peek xs
  let init n f = List.init n f |> ofList
  let isEmpty q = List.isEmpty q.enqueued && List.isEmpty q.toDequeue
  let iter f xs =
    List.iter f xs.toDequeue
    xs.enqueued |> List.rev |> List.iter f
  let iter2 f xs xs' = List.iter2 f (toList xs) (toList xs')
  let iteri f xs =
    List.iteri f xs.toDequeue
    xs.enqueued |> List.rev |> List.iteri f
  let iteri2 f xs xs' = List.iteri2 f (toList xs) (toList xs')
  let length xs =
    List.length xs.enqueued + List.length xs.toDequeue
  let map f xs =
    unsafe_create (List.map f xs.enqueued) (List.map f xs.toDequeue)
  let map2 f xs xs' =
    List.map2 f (toList xs) (toList xs') |> ofList
  let map3 f xs xs' xs'' =
    List.map3 f (toList xs) (toList xs') (toList xs'') |> ofList
  let mapi f xs =
    unsafe_create (List.mapi f xs.enqueued) (List.mapi f xs.toDequeue)
  let mapi2 f xs xs' =
    List.mapi2 f (toList xs) (toList xs') |> ofList
  let nonempty xs = not <| isEmpty xs
  let nth xs pos =
    if pos > xs.toDequeue.Length then
      List.nth (List.rev xs.enqueued) (pos - xs.toDequeue.Length)
    else
      List.nth xs.toDequeue pos
  let fromArray xs = xs |> List.ofArray  |> ofList
  let toArray xs = xs |> toList |> List.toArray
  let fromSeq xs = xs |> List.ofSeq |> ofList
  let toSeq xs = xs |> toList |> List.toSeq
  let min xs = List.min (toList xs)
  let minBy f xs = List.minBy f (toList xs)
  let max xs = List.max (toList xs)
  let maxBy f xs = List.maxBy f (toList xs)
  let partition p xs =
    let enqueuedA, enqueuedB = List.partition p xs.enqueued
    let toDequeueA, toDequeueB = List.partition p xs.toDequeue
    unsafe_create enqueuedA toDequeueA, unsafe_create enqueuedB toDequeueB
  let permute f xs =
    xs |> toList |> List.permute f |> ofList
  let pick p xs =
    match List.tryPick p xs.toDequeue with
    | None -> List.pick p (List.rev xs.enqueued)
    | Some res -> res
  let reduce f xs = xs |> toList |> List.reduce f
  let reduceBack f xs = xs |> toList |> List.reduceBack f
  let replicate n x = List.replicate n x |> ofList
  let rev xs =
    unsafe_create (List.rev xs.toDequeue)  (List.rev xs.enqueued)
  let scan f v xs = xs |> toList |> List.scan f v |> ofList
  let scanBack f xs v = List.scanBack f (toList xs) v |> ofList
  let sort xs = List.sort (toList xs) |> ofList
  let sortBy f xs = List.sortBy f (toList xs) |> ofList
  let sortWith f xs = List.sortWith f (toList xs) |> ofList
  let tail xs  =
    let hd, tl = dequeue xs
    tl
  let tryFind p xs =
    match List.tryFind p xs.toDequeue with
    | None -> List.tryFind p (List.rev xs.enqueued)
    | Some _ as res -> res
  let tryFindIndex p xs =
    match List.tryFindIndex p xs.toDequeue with
    | None -> List.tryFindIndex p (List.rev xs.enqueued)
    | Some _ as res -> res
  let tryPick p xs =
    match List.tryPick p xs.toDequeue with
    | None -> List.tryPick p (List.rev xs.enqueued)
    | Some _ as res -> res

Signature

type Queue<'a> =
  { enqueued : 'a list
    toDequeue : 'a list
  }
    interface System.Collections.IEnumerable
    interface System.Collections.Generic.IEnumerable<'a>

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Queue =
  type t<'a> = Queue<'a>

  val toList : t<'a> -> 'a list
  val ofList : 'a list -> t<'a>
  val empty<'a> : t<'a>
  val peek : t<'a> -> 'a
  val tryPeek : t<'a> -> 'a option
  val enqueue : 'a -> t<'a> -> t<'a>
  val dequeue : t<'a> -> 'a * t<'a>
  val tryDequeue : t<'a> -> ('a * t<'a>) option

  val append : t<'a> -> t<'a> -> t<'a>
  val choose : ('a -> 'b option) -> t<'a> -> t<'b>
  val collect : ('a -> 'b list) -> t<'a> -> t<'b>
  val concat : seq<t<'a>> -> t<'a>
  val exists : ('a -> bool) -> t<'a> -> bool
  val exists2 : ('a -> 'b -> bool) -> t<'a> -> t<'b> -> bool
  val filter : ('a -> bool) -> t<'a> -> t<'a>
  val find : ('a -> bool) -> t<'a> -> 'a
  val findAll : ('a -> bool) -> t<'a> -> t<'a>
  val findIndex : ('a -> bool) -> t<'a> -> int
  val fold : ('a -> 'b -> 'a) -> 'a -> t<'b> -> 'a
  val fold2 : ('a -> 'b -> 'c -> 'a) -> 'a -> t<'b> -> t<'c> -> 'a
  val foldBack : ('a -> 'b -> 'b) -> t<'a> -> 'b -> 'b
  val foldBack2 : ('a -> 'b -> 'c -> 'c) -> t<'a> -> t<'b> -> 'c -> 'c
  val forAll : ('a -> bool) -> t<'a> -> bool
  val forAll2 : ('a -> 'b -> bool) -> t<'a> -> t<'b> -> bool
  val head : t<'a> -> 'a
  val init : int -> (int -> 'a) -> t<'a>
  val isEmpty : t<'a> -> bool
  val iter : ('a -> unit) -> t<'a> -> unit
  val iter2 : ('a -> 'b -> unit) -> t<'a> -> t<'b> -> unit
  val iteri : (int -> 'a -> unit) -> t<'a> -> unit
  val iteri2 : (int -> 'a -> 'b -> unit) -> t<'a> -> t<'b> -> unit
  val length : t<'a> -> int
  val map : ('a -> 'b) -> t<'a> -> t<'b>
  val map2 : ('a -> 'b -> 'c) -> t<'a> -> t<'b> -> t<'c>
  val map3 : ('a -> 'b -> 'c -> 'd) -> t<'a> -> t<'b> -> t<'c> -> t<'d>
  val mapi : (int -> 'a -> 'b) -> t<'a> -> t<'b>
  val mapi2 : (int -> 'a -> 'b -> 'c) -> t<'a> -> t<'b> -> t<'c>
  val min : t<'a> -> 'a when 'a:comparison
  val minBy : ('a -> 'b) -> t<'a> -> 'a when 'b:comparison
  val max : t<'a> -> 'a when 'a:comparison
  val maxBy : ('a -> 'b) -> t<'a> -> 'a when 'b:comparison
  val nonempty : t<'a> -> bool
  val nth : t<'a> -> int -> 'a
  val fromArray : 'a array -> t<'a>
  val toArray : t<'a> -> 'a array
  val fromSeq : seq<'b> -> t<'b>
  val toSeq : t<'a> -> seq<'a>
  val partition : ('a -> bool) -> t<'a> -> t<'a> * t<'a>
  val permute : (int -> int) -> t<'a> -> t<'a>
  val pick : ('a -> option<'b>) -> t<'a> -> 'b
  val reduce : ('a -> 'a -> 'a) -> t<'a> -> 'a
  val reduceBack : ('a -> 'a -> 'a) -> t<'a> -> 'a
  val replicate : int -> 'a -> t<'a>
  val rev : t<'a> -> t<'a>
  val scan : ('a -> 'b -> 'a) -> 'a -> t<'b> -> t<'a>
  val scanBack : ('a -> 'b -> 'b) -> t<'a> -> 'b -> t<'b>
  val sort : t<'a> -> t<'a> when 'a: comparison
  val sortBy : ('a -> 'b) -> t<'a> -> t<'a> when 'b: comparison
  val sortWith : ('a -> 'a -> int) -> t<'a> -> t<'a>
  val tail : t<'a> -> t<'a>
  val tryFind : ('a -> bool) -> t<'a> -> option<'a>
  val tryFindIndex : ('a -> bool) -> t<'a> -> option<int>
  val tryPick : ('a -> 'b option) -> t<'a> -> option<'b>

One Response to “An immutable queue library in F#”

  1. Rick Minerich says:

    Very cool. You should submit this to the F# Power Pack.