apply-merge-0.1.0.0: src/Data/DoublyLinkedList/STRef.hs
-- SPDX-FileCopyrightText: Copyright Preetham Gujjula
-- SPDX-License-Identifier: BSD-3-Clause
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedRecordDot #-}
module Data.DoublyLinkedList.STRef
( -- * Types
DoublyLinkedList,
DoublyLinkedNode,
-- * Construction
empty,
fromList,
-- * Traversal
head,
last,
next,
prev,
-- * Query
null,
value,
-- * Insertion
cons,
snoc,
insertBefore,
insertAfter,
-- * Deletion
delete,
-- * List conversion
toList,
)
where
import Control.Arrow ((>>>))
import Control.Monad (forM_)
import Control.Monad.ST (ST)
import Data.STRef (STRef, newSTRef, readSTRef, writeSTRef)
import Prelude hiding (head, last, null)
data DoublyLinkedList s a = DoublyLinkedList
{ firstNode :: FirstNode s a,
lastNode :: LastNode s a
}
-- | The sentinel node at the beginning of the list.
newtype FirstNode s a = FirstNode {nextRef :: STRef s (NextNode s a)}
data DoublyLinkedNode s a = DoublyLinkedNode
{ _value :: a,
nextRef :: STRef s (NextNode s a),
prevRef :: STRef s (PrevNode s a)
}
-- | The sentinel node at the end of the list.
newtype LastNode s a = LastNode
{ prevRef :: STRef s (PrevNode s a)
}
-- | Type for nodes that are after other nodes.
newtype NextNode s a = NextNode
{ unNextNode :: Either (DoublyLinkedNode s a) (LastNode s a)
}
nextNodeToDoublyLinkedNode :: NextNode s a -> Maybe (DoublyLinkedNode s a)
nextNodeToDoublyLinkedNode (NextNode (Left v)) = Just v
nextNodeToDoublyLinkedNode _ = Nothing
-- | Type for nodes that are before other nodes.
newtype PrevNode s a = PrevNode
{ unPrevNode :: Either (FirstNode s a) (DoublyLinkedNode s a)
}
class HasNext node where
next1 :: node s a -> ST s (NextNode s a)
next1 = let x = x in x
setNext1 :: (HasPrev next_node) => node s a -> next_node s a -> ST s ()
setNext1 = let x = x in x
toPrev1 :: node s a -> PrevNode s a
toPrev1 = let x = x in x
-- Abuse the MINIMAL pragma to not include hidden functions in Haddocks
{-# MINIMAL #-}
instance HasNext FirstNode where
next1 (FirstNode nextRef) = readSTRef nextRef
{-# INLINE next1 #-}
setNext1 (FirstNode nextRef) nextNode = writeSTRef nextRef (toNext1 nextNode)
{-# INLINE setNext1 #-}
toPrev1 = PrevNode . Left
{-# INLINE toPrev1 #-}
instance HasNext DoublyLinkedNode where
next1 valueNode = readSTRef valueNode.nextRef
{-# INLINE next1 #-}
setNext1 valueNode nextNode = writeSTRef valueNode.nextRef (toNext1 nextNode)
{-# INLINE setNext1 #-}
toPrev1 = PrevNode . Right
{-# INLINE toPrev1 #-}
instance HasNext PrevNode where
next1 = either next1 next1 . unPrevNode
{-# INLINE next1 #-}
setNext1 prevNode nextNode =
either (`setNext1` nextNode) (`setNext1` nextNode) (unPrevNode prevNode)
{-# INLINE setNext1 #-}
toPrev1 = id
{-# INLINE toPrev1 #-}
class HasPrev node where
prev1 :: node s a -> ST s (PrevNode s a)
prev1 = let x = x in x
setPrev1 :: (HasNext prev_node) => node s a -> prev_node s a -> ST s ()
setPrev1 = let x = x in x
toNext1 :: node s a -> NextNode s a
toNext1 = let x = x in x
-- Abuse the MINIMAL pragma to not include hidden functions in Haddocks
{-# MINIMAL #-}
instance HasPrev DoublyLinkedNode where
prev1 valueNode = readSTRef valueNode.prevRef
{-# INLINE prev1 #-}
setPrev1 valueNode prevNode = writeSTRef valueNode.prevRef (toPrev1 prevNode)
{-# INLINE setPrev1 #-}
toNext1 = NextNode . Left
{-# INLINE toNext1 #-}
instance HasPrev LastNode where
prev1 (LastNode prevRef) = readSTRef prevRef
{-# INLINE prev1 #-}
setPrev1 (LastNode prevRef) prevNode = writeSTRef prevRef (toPrev1 prevNode)
{-# INLINE setPrev1 #-}
toNext1 = NextNode . Right
{-# INLINE toNext1 #-}
instance HasPrev NextNode where
prev1 = either prev1 prev1 . unNextNode
{-# INLINE prev1 #-}
setPrev1 nextNode prevNode =
either (`setPrev1` prevNode) (`setPrev1` prevNode) (unNextNode nextNode)
{-# INLINE setPrev1 #-}
toNext1 = id
{-# INLINE toNext1 #-}
null :: DoublyLinkedList s a -> ST s Bool
null list = do
nextNode <- readSTRef list.firstNode.nextRef
case nextNode of
NextNode (Left _) -> pure False
_ -> pure True
{-# INLINE null #-}
value :: DoublyLinkedNode s a -> a
value = _value
{-# INLINE value #-}
head :: DoublyLinkedList s a -> ST s (Maybe (DoublyLinkedNode s a))
head list =
readSTRef list.firstNode.nextRef >>= \case
NextNode (Left doublyLinkedNode) -> pure (Just doublyLinkedNode)
_ -> pure Nothing
last :: DoublyLinkedList s a -> ST s (Maybe (DoublyLinkedNode s a))
last list =
readSTRef list.lastNode.prevRef >>= \case
PrevNode (Right doublyLinkedNode) -> pure (Just doublyLinkedNode)
_ -> pure Nothing
next :: DoublyLinkedNode s a -> ST s (Maybe (DoublyLinkedNode s a))
next valueNode = do
nextNode <- readSTRef valueNode.nextRef
pure $ case nextNode of
NextNode (Left valueNode') -> Just valueNode'
_ -> Nothing
prev :: DoublyLinkedNode s a -> ST s (Maybe (DoublyLinkedNode s a))
prev valueNode = do
prevNode <- readSTRef valueNode.prevRef
pure $ case prevNode of
PrevNode (Right valueNode') -> Just valueNode'
_ -> Nothing
empty :: ST s (DoublyLinkedList s a)
empty = do
firstNode <- FirstNode <$> newSTRef undefined
lastNode <- LastNode <$> newSTRef undefined
writeSTRef firstNode.nextRef (NextNode (Right lastNode))
writeSTRef lastNode.prevRef (PrevNode (Left firstNode))
pure (DoublyLinkedList firstNode lastNode)
insertAfter1 :: (HasNext node) => node s a -> a -> ST s (DoublyLinkedNode s a)
insertAfter1 nodeA x = do
nodeC <- next1 nodeA
nodeB <- DoublyLinkedNode x <$> newSTRef nodeC <*> newSTRef (toPrev1 nodeA)
setNext1 nodeA nodeB
setPrev1 nodeC nodeB
pure nodeB
insertBefore1 :: (HasPrev node) => node s a -> a -> ST s (DoublyLinkedNode s a)
insertBefore1 nodeC x = do
nodeA <- prev1 nodeC
nodeB <- DoublyLinkedNode x <$> newSTRef (toNext1 nodeC) <*> newSTRef nodeA
setNext1 nodeA nodeB
setPrev1 nodeC nodeB
pure nodeB
insertBefore :: DoublyLinkedNode s a -> a -> ST s (DoublyLinkedNode s a)
insertBefore = insertBefore1
insertAfter :: DoublyLinkedNode s a -> a -> ST s (DoublyLinkedNode s a)
insertAfter = insertAfter1
cons :: DoublyLinkedList s a -> a -> ST s (DoublyLinkedNode s a)
cons list = insertAfter1 list.firstNode
{-# INLINE cons #-}
snoc :: DoublyLinkedList s a -> a -> ST s (DoublyLinkedNode s a)
snoc list = insertBefore1 list.lastNode
{-# INLINE snoc #-}
delete :: DoublyLinkedNode s a -> ST s ()
delete nodeB = do
nodeA <- prev1 nodeB
nodeC <- next1 nodeB
setNext1 nodeA nodeC
setPrev1 nodeC nodeA
fromList :: [a] -> ST s (DoublyLinkedList s a)
fromList xs = do
list <- empty
forM_ xs $ \x ->
snoc list x
pure list
toList :: DoublyLinkedList s a -> ST s [a]
toList list = next1 list.firstNode >>= go
where
go :: NextNode s a -> ST s [a]
go =
nextNodeToDoublyLinkedNode >>> \case
Nothing -> pure []
Just valueNode -> (valueNode._value :) <$> (next1 valueNode >>= go)