{-# LANGUAGE Trustworthy #-}
-- |
-- Copyright: (c) 2021 Xy Ren
-- License: BSD3
-- Maintainer: xy.r@outlook.com
-- Stability: experimental
-- Portability: non-portable (GHC only)
module Cleff.Input
  ( -- * Effect
    Input (..)
  , -- * Operations
    input, inputs
  , -- * Interpretations
    runInputConst, inputToListState, inputToReader, runInputEff, mapInput, bindInput
  ) where

import           Cleff
import           Cleff.Reader
import           Cleff.State

-- * Effect

-- | An effect that is capable of reading from some input source, such as an input stream.
data Input i :: Effect where
  Input :: Input i m i

-- * Operations

makeEffect ''Input

-- | Apply a function to the result of 'input'.
inputs :: Input i :> es => (i -> i') -> Eff es i'
inputs :: (i -> i') -> Eff es i'
inputs i -> i'
f = i -> i'
f (i -> i') -> Eff es i -> Eff es i'
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> Eff es i
forall i (es :: [(Type -> Type) -> Type -> Type]).
(Input i :> es) =>
Eff es i
input

-- * Interpretations

-- | Run an 'Input' effect by giving a constant input value.
runInputConst :: i -> Eff (Input i ': es) ~> Eff es
runInputConst :: i -> Eff (Input i : es) ~> Eff es
runInputConst i
x = Handler (Input i) es -> Eff (Input i : es) ~> Eff es
forall (e :: (Type -> Type) -> Type -> Type)
       (es :: [(Type -> Type) -> Type -> Type]).
Handler e es -> Eff (e : es) ~> Eff es
interpret \case
  Input i (Eff esSend) a
Input -> i -> Eff es i
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure i
x
{-# INLINE runInputConst #-}

-- | Run an 'Input' effect by going through a list of values.
inputToListState :: Eff (Input (Maybe i) ': es) ~> Eff (State [i] ': es)
inputToListState :: Eff (Input (Maybe i) : es) a -> Eff (State [i] : es) a
inputToListState = Handler (Input (Maybe i)) (State [i] : es)
-> Eff (Input (Maybe i) : es) ~> Eff (State [i] : es)
forall (e' :: (Type -> Type) -> Type -> Type)
       (e :: (Type -> Type) -> Type -> Type)
       (es :: [(Type -> Type) -> Type -> Type]).
Handler e (e' : es) -> Eff (e : es) ~> Eff (e' : es)
reinterpret \case
  Input (Maybe i) (Eff esSend) a
Input -> ([i] -> (Maybe i, [i])) -> Eff (State [i] : es) (Maybe i)
forall s (es :: [(Type -> Type) -> Type -> Type]) a.
(State s :> es) =>
(s -> (a, s)) -> Eff es a
state \case
    []     -> (Maybe i
forall a. Maybe a
Nothing, [])
    i
x : [i]
xs -> (i -> Maybe i
forall a. a -> Maybe a
Just i
x, [i]
xs)
{-# INLINE inputToListState #-}

-- | Run an 'Input' in terms of a 'Reader'.
--
-- @since 0.2.1.0
inputToReader :: Eff (Input i ': es) ~> Eff (Reader i ': es)
inputToReader :: Eff (Input i : es) a -> Eff (Reader i : es) a
inputToReader = Handler (Input i) (Reader i : es)
-> Eff (Input i : es) ~> Eff (Reader i : es)
forall (e' :: (Type -> Type) -> Type -> Type)
       (e :: (Type -> Type) -> Type -> Type)
       (es :: [(Type -> Type) -> Type -> Type]).
Handler e (e' : es) -> Eff (e : es) ~> Eff (e' : es)
reinterpret \case
  Input i (Eff esSend) a
Input -> Eff (Reader i : es) a
forall r (es :: [(Type -> Type) -> Type -> Type]).
(Reader r :> es) =>
Eff es r
ask
{-# INLINE inputToReader #-}

-- | Run an 'Input' effect by performing a computation for each input request.
runInputEff :: Eff es i -> Eff (Input i ': es) ~> Eff es
runInputEff :: Eff es i -> Eff (Input i : es) ~> Eff es
runInputEff Eff es i
m = Handler (Input i) es -> Eff (Input i : es) ~> Eff es
forall (e :: (Type -> Type) -> Type -> Type)
       (es :: [(Type -> Type) -> Type -> Type]).
Handler e es -> Eff (e : es) ~> Eff es
interpret \case
  Input i (Eff esSend) a
Input -> Eff es i
Eff es a
m
{-# INLINE runInputEff #-}

-- | Transform an 'Input' effect into another one already in the effect stack, by a pure function.
--
-- @since 0.2.1.0
mapInput :: Input i' :> es => (i' -> i) -> Eff (Input i ': es) ~> Eff es
mapInput :: (i' -> i) -> Eff (Input i : es) ~> Eff es
mapInput i' -> i
f = Handler (Input i) es -> Eff (Input i : es) ~> Eff es
forall (e :: (Type -> Type) -> Type -> Type)
       (es :: [(Type -> Type) -> Type -> Type]).
Handler e es -> Eff (e : es) ~> Eff es
interpret \case
  Input i (Eff esSend) a
Input -> i' -> i
f (i' -> i) -> Eff es i' -> Eff es i
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> Eff es i'
forall i (es :: [(Type -> Type) -> Type -> Type]).
(Input i :> es) =>
Eff es i
input
{-# INLINE mapInput #-}

-- | Transform an 'Input' effect into another one already in the effect stack, by an effectful computation.
--
-- @since 0.2.1.0
bindInput :: Input i' :> es => (i' -> Eff es i) -> Eff (Input i ': es) ~> Eff es
bindInput :: (i' -> Eff es i) -> Eff (Input i : es) ~> Eff es
bindInput i' -> Eff es i
f = Handler (Input i) es -> Eff (Input i : es) ~> Eff es
forall (e :: (Type -> Type) -> Type -> Type)
       (es :: [(Type -> Type) -> Type -> Type]).
Handler e es -> Eff (e : es) ~> Eff es
interpret \case
  Input i (Eff esSend) a
Input -> i' -> Eff es i
f (i' -> Eff es i) -> Eff es i' -> Eff es i
forall (m :: Type -> Type) a b. Monad m => (a -> m b) -> m a -> m b
=<< Eff es i'
forall i (es :: [(Type -> Type) -> Type -> Type]).
(Input i :> es) =>
Eff es i
input
{-# INLINE bindInput #-}