packages feed

moffy-0.1.0.0: src/Control/Moffy/Handle.hs

{-# LANGUAGE LambdaCase, TupleSections #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE TypeOperators, ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Control.Moffy.Handle (
	-- * Constraint
	ExpandableHandle, ExpandableOccurred, MergeableOccurred,
	-- * Plain Handle
	-- ** Type
	Handle, Handle',
	-- ** Composer
	retry, sleep, sleepIfNothing, expand, before, merge,
	-- * Handle with State
	-- ** Type
	HandleSt, HandleSt', St, liftHandle, liftHandle', liftSt,
	-- ** Composer
	retrySt, expandSt, beforeSt, mergeSt,
	-- * Handle with Input and Output
	-- ** Type
	HandleIo', pushInput, popInput,
	-- ** Composer
	expandIo, beforeIo, mergeIo ) where

import Control.Arrow (first)
import Control.Moffy.Internal.React.Type (
	EvReqs, EvOccs, ExpandableOccurred, MergeableOccurred,
	Handle, HandleSt, St, liftHandle, liftSt )
import Control.Concurrent (threadDelay)
import Data.Type.Set ((:+:))
import Data.OneOrMore (Collapsable)
import Data.OneOrMoreApp (merge')

import qualified Data.OneOrMore as OOM (collapse)
import qualified Data.OneOrMoreApp as OOM (expand)

---------------------------------------------------------------------------

-- * CONSTRAINT
-- * PLAIN HANDLE
--	+ TYPE
--	+ COMPOSER
-- * HANDLE WITH STATE
--	+ TYPE
--	+ COMPOSER
-- * HANDLE WITH INPUT AND OUTPUT
--	+ TYPE
--	+ COMPOSER

---------------------------------------------------------------------------
-- CONSTRAINT
---------------------------------------------------------------------------

type ExpandableHandle es es' = (ExpandableOccurred es es', Collapsable es' es)

---------------------------------------------------------------------------
-- PLAIN HANDLE
---------------------------------------------------------------------------

-- TYPE

type Handle' m es = EvReqs es -> m (Maybe (EvOccs es))

-- COMPOSER

retry :: Monad m => Handle' m es -> Handle m es
retry hdl rqs = retry hdl rqs `maybe` pure =<< hdl rqs

sleep :: Int -> Handle' IO es -> Handle' IO es
sleep n hdl rqs = hdl rqs <* threadDelay n

sleepIfNothing :: Int -> Handle' IO es -> Handle' IO es
sleepIfNothing n hdl rqs = hdl rqs >>= \case
	Nothing -> Nothing <$ threadDelay n
	Just x -> pure $ Just x

collapse :: (Applicative m, Collapsable es' es) =>
	Handle' m es -> EvReqs es' -> m (Maybe (EvOccs es))
collapse hdl = (pure Nothing `maybe` hdl) . OOM.collapse

expand :: (Applicative m, ExpandableHandle es es') =>
	Handle' m es -> Handle' m es'
expand hdl = ((OOM.expand <$>) <$>) . collapse hdl

infixr 5 `before`, `beforeSt`

before :: (
	Monad m,
	ExpandableHandle es (es :+: es'), ExpandableHandle es' (es :+: es') ) =>
	Handle' m es -> Handle' m es' -> Handle' m (es :+: es')
((expand -> l) `before` (expand -> r)) rqs =
	r rqs `maybe` (pure . Just) =<< l rqs

infixr 6 `merge`, `mergeSt`

merge :: (
	Applicative m,
	ExpandableHandle es (es :+: es'), ExpandableHandle es' (es :+: es'),
	MergeableOccurred es es' (es :+: es') ) =>
	Handle' m es -> Handle' m es' -> Handle' m (es :+: es')
((collapse -> l) `merge` (collapse -> r)) rqs = merge' <$> l rqs <*> r rqs

---------------------------------------------------------------------------
-- HANDLE WITH STATE
---------------------------------------------------------------------------

-- TYPE

type HandleSt' st m es = EvReqs es -> St st m (Maybe (EvOccs es))
-- ^ > type HandleSt' st m es = HandleIo' st st m es

liftHandle' :: Functor m => Handle' m es -> HandleSt' st m es
liftHandle' = (liftSt .)

-- COMPOSER

retrySt :: Monad m => HandleSt' st m es -> HandleSt st m es
retrySt hdl rqs st = hdl rqs st >>= \(mo, st') ->
	(retrySt hdl rqs st' `maybe` (pure . (, st'))) mo

expandSt :: (Applicative m, ExpandableHandle es es') =>
	HandleSt' st m es -> HandleSt' st m es'
expandSt = (`expandIo` pure)

beforeSt :: (
	Monad m,
	ExpandableHandle es (es :+: es'), ExpandableHandle es' (es :+: es') ) =>
	HandleSt' st m es -> HandleSt' st m es' -> HandleSt' st m (es :+: es')
l `beforeSt` r = beforeIo l pure r pure

mergeSt :: (
	Monad m,
	ExpandableHandle es (es :+: es'), ExpandableHandle es' (es :+: es'),
	MergeableOccurred es es' (es :+: es') ) =>
	HandleSt' st m es -> HandleSt' st m es' -> HandleSt' st m (es :+: es')
l `mergeSt` r = mergeIo l pure r pure

---------------------------------------------------------------------------
-- HANDLE WITH INPUT AND OUTPUT
---------------------------------------------------------------------------

-- TYPE

type HandleIo' i o m es = EvReqs es -> i -> m (Maybe (EvOccs es), o)

pushInput :: (a -> HandleSt' st m es) -> HandleIo' (a, st) st m es
pushInput = (uncurry .) . flip

popInput :: HandleIo' (a, st) st m es -> a -> HandleSt' st m es
popInput = flip . (curry .)

-- COMPOSER

collapseIo :: (Applicative m, Collapsable es' es) =>
	HandleIo' i o m es -> (i -> m o) ->
	EvReqs es' -> i -> m (Maybe (EvOccs es), o)
collapseIo hdl nh = ((((Nothing ,) <$>) . nh) `maybe` hdl) . OOM.collapse

expandIo :: (Applicative m, ExpandableHandle es es') =>
	HandleIo' i o m es -> (i -> m o) -> HandleIo' i o m es'
expandIo hdl nh rqs = (first (OOM.expand <$>) <$>) . collapseIo hdl nh rqs

beforeIo :: (
	Monad m,
	ExpandableHandle es (es :+: es'), ExpandableHandle es' (es :+: es') ) =>
	HandleIo' i x m es -> (i -> m x) ->
	HandleIo' x o m es' -> (x -> m o) -> HandleIo' i o m (es :+: es')
beforeIo l nhl r nhr rqs st = expandIo l nhl rqs st >>= \(mo, st') ->
	(expandIo r nhr rqs st' `maybe` ((<$> nhr st') . (,) . Just)) mo

mergeIo :: (
	Monad m,
	ExpandableHandle es (es :+: es'), ExpandableHandle es' (es :+: es'),
	MergeableOccurred es es' (es :+: es') ) =>
	HandleIo' i x m es -> (i -> m x) ->
	HandleIo' x o m es' -> (x -> m o) -> HandleIo' i o m (es :+: es')
mergeIo l nhl r nhr rqs st = collapseIo l nhl rqs st >>= \(mo, st') ->
	first (mo `merge'`) <$> collapseIo r nhr rqs st'