packages feed

potoki-core-0.9: library/Potoki/Core/Transform.hs

module Potoki.Core.Transform
(
  Transform(..),
  consume,
  mapFetch,
  executeIO,
  take,
)
where

import Potoki.Core.Prelude hiding (take)
import Potoki.Core.Types
import qualified Potoki.Core.Fetch as A
import qualified Potoki.Core.Consume as C
import qualified Potoki.Core.Produce as D
import qualified Potoki.Core.IO as E


instance Category Transform where
  id =
    Transform (return id)
  (.) (Transform left) (Transform right) =
    Transform ((.) <$> left <*> right)

instance Profunctor Transform where
  dimap inputMapping outputMapping (Transform managed) =
    Transform $ do
      newFetch <- managed
      return $ \ oldFetch -> fmap outputMapping (newFetch (fmap inputMapping oldFetch))

instance Choice Transform where
  right' (Transform rightTransformManaged) =
    Transform $ do
      rightInFetchToOutFetch <- rightTransformManaged
      fetchedLeftMaybeRef <- liftIO (newIORef Nothing)
      return $ \ inFetch ->
        let
          Fetch rightFetchIO = rightInFetchToOutFetch (A.rightHandlingLeft (writeIORef fetchedLeftMaybeRef . Just) inFetch)
          in Fetch $ \ stop yield -> do
            join $ rightFetchIO
              (do
                fetchedLeftMaybe <- readIORef fetchedLeftMaybeRef
                case fetchedLeftMaybe of
                  Just fetchedLeft -> do
                    writeIORef fetchedLeftMaybeRef Nothing
                    return (yield (Left fetchedLeft))
                  Nothing -> return stop)
              (\ right -> return (yield (Right right)))

instance Strong Transform where
  first' (Transform firstTransformManaged) =
    Transform $ do
      cacheRef <- liftIO (newIORef undefined)
      firstInFetchToOutFetch <- firstTransformManaged
      return (A.bothFetchingFirst cacheRef . firstInFetchToOutFetch . A.firstCachingSecond cacheRef)

instance Arrow Transform where
  arr fn =
    Transform (return (fmap fn))
  first =
    first'

instance ArrowChoice Transform where
  left =
    left'

{-# INLINE consume #-}
consume :: Consume input output -> Transform input output
consume (Consume runFetch) =
  Transform $ do
    stoppedRef <- liftIO (newIORef False)
    return $ \ (Fetch fetch) -> Fetch $ \ stop yield -> do
      stopped <- readIORef stoppedRef
      if stopped
        then do
          writeIORef stoppedRef False
          return stop
        else do
          emittedRef <- newIORef False
          output <-
            runFetch $ Fetch $ \ inputNil inputJust ->
            join
              (fetch
                (do
                  writeIORef stoppedRef True
                  return inputNil)
                (\ !input -> do
                  writeIORef emittedRef True
                  return (inputJust input)))
          stopped <- readIORef stoppedRef
          if stopped
            then do
              emitted <- readIORef emittedRef
              if emitted
                then return (yield output)
                else do
                  writeIORef stoppedRef False
                  return stop
            else return (yield output)

{-# INLINE mapFetch #-}
mapFetch :: (Fetch a -> Fetch b) -> Transform a b
mapFetch mapping =
  Transform $ return mapping

{-|
Execute the IO action.
-}
{-# INLINE executeIO #-}
executeIO :: Transform (IO a) a
executeIO =
  mapFetch $ \ (Fetch fetchIO) -> Fetch $ \ stop yield ->
  join (fetchIO (return stop) (fmap yield))

{-# INLINE take #-}
take :: Int -> Transform input input
take amount =
  Transform $ do
    countRef <- liftIO (newIORef amount)
    return $ \ (Fetch fetchIO) -> Fetch $ \ stop yield -> do
      count <- readIORef countRef
      if count > 0
        then do
          writeIORef countRef $! pred count
          fetchIO stop yield
        else return stop