linear-base-0.2.0: examples/Foreign/Heap.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LinearTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE NoImplicitPrelude #-}
-- | Implementation of pairing heaps stored off-heap
module Foreign.Heap where
import qualified Data.List as List
import Foreign.List (List)
import qualified Foreign.List as List
import Foreign.Marshal.Pure (Box, Pool)
import qualified Foreign.Marshal.Pure as Manual
import Prelude.Linear hiding (foldl)
data Heap k a
= Empty
| NonEmpty (Box (NEHeap k a))
data NEHeap k a
= Heap k a (Box (List (NEHeap k a)))
instance
(Manual.Representable k, Manual.Representable a) =>
Manual.MkRepresentable (NEHeap k a) (k, a, Box (List (NEHeap k a)))
where
toRepr (Heap k a l) = (k, a, l)
ofRepr (k, a, l) = Heap k a l
instance (Manual.Representable k, Manual.Representable a) => Manual.Representable (NEHeap k a) where
type AsKnown (NEHeap k a) = Manual.AsKnown (k, a, (Box (List (NEHeap k a))))
-- * Non-empty heap primitives
singletonN :: (Manual.Representable k, Manual.Representable a) => k %1 -> a %1 -> Pool %1 -> NEHeap k a
singletonN k a pool = Heap k a (Manual.alloc List.Nil pool)
-- XXX: (Movable k, Ord k) is a bit stronger than strictly required. We could
-- give a linear version of `Ord` instead.
mergeN :: forall k a. (Manual.Representable k, Manual.Representable a, Movable k, Ord k) => NEHeap k a %1 -> NEHeap k a %1 -> Pool %1 -> NEHeap k a
mergeN (Heap k1 a1 h1) (Heap k2 a2 h2) pool =
testAndRebuild (move k1) a1 h1 (move k2) a2 h2 pool
where
--- XXX: this is a good example of why we need a working `case` and/or
--- `let`.
testAndRebuild :: Ur k %1 -> a %1 -> Box (List (NEHeap k a)) %1 -> Ur k %1 -> a %1 -> Box (List (NEHeap k a)) %1 -> Pool %1 -> NEHeap k a
testAndRebuild (Ur k1') a1' h1' (Ur k2') a2' h2' =
if k1' <= k2'
then helper k1' a1' k2' a2' h1' h2'
else helper k2' a2' k1' a1' h2' h1'
helper :: k -> a %1 -> k -> a %1 -> Box (List (NEHeap k a)) %1 -> Box (List (NEHeap k a)) %1 -> Pool %1 -> NEHeap k a
helper k1'' a1'' k2'' a2'' h1'' h2'' pool'' = Heap k1'' a1'' (Manual.alloc ((List.Cons :: b %1 -> Box (List b) %1 -> List b) ((Heap :: c %1 -> b %1 -> Box (List (NEHeap c b)) %1 -> NEHeap c b) k2'' a2'' h2'') h1'') pool'')
-- XXX: the type signatures for List.Cons and Heap are necessary for certain
-- older versions of the compiler, and as such are temporary. See PR #38
-- and PR #380 in tweag/ghc/linear-types.
mergeN' :: forall k a. (Manual.Representable k, Manual.Representable a, Movable k, Ord k) => NEHeap k a %1 -> Heap k a %1 -> Pool %1 -> NEHeap k a
mergeN' h Empty pool = pool `lseq` h
mergeN' h (NonEmpty h') pool = mergeN h (Manual.deconstruct h') pool
extractMinN :: (Manual.Representable k, Manual.Representable a, Movable k, Ord k) => NEHeap k a %1 -> Pool %1 -> (k, a, Heap k a)
extractMinN (Heap k a h) pool = (k, a, pairUp (Manual.deconstruct h) pool)
pairUp :: forall k a. (Manual.Representable k, Manual.Representable a, Movable k, Ord k) => List (NEHeap k a) %1 -> Pool %1 -> Heap k a
pairUp List.Nil pool = pool `lseq` Empty
pairUp (List.Cons h r) pool = pairOne h (Manual.deconstruct r) (dup pool)
where
pairOne :: NEHeap k a %1 -> List (NEHeap k a) %1 -> (Pool, Pool) %1 -> Heap k a
pairOne h' r' (pool1, pool2) =
NonEmpty $ Manual.alloc (pairOne' h' r' (dup3 pool1)) pool2
pairOne' :: NEHeap k a %1 -> List (NEHeap k a) %1 -> (Pool, Pool, Pool) %1 -> NEHeap k a
pairOne' h1 List.Nil pools =
pools `lseq` h1
pairOne' h1 (List.Cons h2 r') (pool1, pool2, pool3) =
mergeN' (mergeN h1 h2 pool1) (pairUp (Manual.deconstruct r') pool2) pool3
-- * Heap primitives
empty :: Heap k a
empty = Empty
singleton :: forall k a. (Manual.Representable k, Manual.Representable a) => k %1 -> a %1 -> Pool %1 -> Heap k a
singleton k a pool = NonEmpty $ singletonAlloc k a (dup pool)
where
singletonAlloc :: k %1 -> a %1 -> (Pool, Pool) %1 -> Box (NEHeap k a)
singletonAlloc k' a' (pool1, pool2) =
Manual.alloc (singletonN k' a' pool1) pool2
extractMin :: (Manual.Representable k, Manual.Representable a, Movable k, Ord k) => Heap k a %1 -> Pool %1 -> Maybe (k, a, Heap k a)
extractMin Empty pool = pool `lseq` Nothing
extractMin (NonEmpty h) pool = Just $ extractMinN (Manual.deconstruct h) pool
merge :: forall k a. (Manual.Representable k, Manual.Representable a, Movable k, Ord k) => Heap k a %1 -> Heap k a %1 -> Pool %1 -> Heap k a
merge Empty h' pool = pool `lseq` h'
merge (NonEmpty h) h' pool = NonEmpty $ neMerge (Manual.deconstruct h) h' (dup pool)
where
neMerge :: NEHeap k a %1 -> Heap k a %1 -> (Pool, Pool) %1 -> Box (NEHeap k a)
neMerge h1 h2 (pool1, pool2) =
Manual.alloc (mergeN' h1 h2 pool1) pool2
-- * Heap sort
-- | Guaranteed to yield pairs in ascending key order
foldl :: forall k a b. (Manual.Representable k, Manual.Representable a, Movable k, Ord k) => (b %1 -> k %1 -> a %1 -> b) -> b %1 -> Heap k a %1 -> Pool %1 -> b
foldl f acc h pool = go acc h (dup pool)
where
go :: b %1 -> Heap k a %1 -> (Pool, Pool) %1 -> b
go acc' h' (pool1, pool2) = dispatch acc' (extractMin h' pool1) pool2
dispatch :: b %1 -> Maybe (k, a, Heap k a) %1 -> Pool %1 -> b
dispatch acc' Nothing pool' = pool' `lseq` acc'
dispatch acc' (Just (k, a, h')) pool' =
foldl f (f acc' k a) h' pool'
-- | Strict: stream must terminate.
unfold :: forall k a s. (Manual.Representable k, Manual.Representable a, Movable k, Ord k) => (s -> Maybe ((k, a), s)) -> s -> Pool %1 -> Heap k a
unfold step seed pool = dispatch (step seed) pool
where
dispatch :: (Maybe ((k, a), s)) -> Pool %1 -> Heap k a
dispatch Nothing pool' = pool' `lseq` Empty
dispatch (Just ((k, a), next)) pool' = mkStep k a next (dup3 pool')
mkStep :: k -> a -> s -> (Pool, Pool, Pool) %1 -> Heap k a
mkStep k a next (pool1, pool2, pool3) =
merge (singleton k a pool1) (unfold step next pool2) pool3
-- TODO: linear unfold: could apply to off-heap lists!
ofList :: (Manual.Representable k, Manual.Representable a, Movable k, Ord k) => [(k, a)] -> Pool %1 -> Heap k a
ofList l pool = unfold List.uncons l pool
-- XXX: sorts in reverse
toList :: (Manual.Representable k, Manual.Representable a, Movable k, Ord k) => Heap k a %1 -> Pool %1 -> [(k, a)]
toList h pool = foldl (\l k a -> (k, a) : l) [] h pool
sort :: forall k a. (Manual.Representable k, Manual.Representable a, Movable k, Ord k, Movable a) => [(k, a)] -> [(k, a)]
sort l = unur $ Manual.withPool (\pool -> move $ sort' l (dup pool))
where
-- XXX: can we avoid this call to `move`?
sort' :: [(k, a)] -> (Pool, Pool) %1 -> [(k, a)]
sort' l' (pool1, pool2) = toList (ofList l' pool1) pool2