{-# options_haddock prune #-}

-- |Description: Process Effect, Internal
module Polysemy.Process.Effect.Process where

import Polysemy.Conc.Effect.Scoped (Scoped, Scoped_, scoped, scoped_)
import Polysemy.Input (Input (Input))
import Polysemy.Output (Output (Output))
import Polysemy.Resume (interpretResumable, restop, type (!!))
import Prelude hiding (send)

-- |Abstraction of a process with input and output.
--
-- This effect is intended to be used in a scoped_ manner:
--
-- @
-- import Polysemy.Resume
-- import Polysemy.Conc
-- import Polysemy.Process
-- import qualified System.Process.Typed as System
--
-- prog :: Member (Scoped_ (Process Text Text !! err)) r => Sem r Text
-- prog =
--  resumeAs "failed" do
--    withProcess do
--      send "input"
--      recv
--
-- main :: IO ()
-- main = do
--   out <- runConc $ interpretProcessNative (System.proc "cat" []) prog
--   putStrLn out
-- @
data Process i o :: Effect where
  Recv :: Process i o m o
  Send :: i -> Process i o m ()

makeSem_ ''Process

-- |Obtain a chunk of output.
recv ::
   i o r .
  Member (Process i o) r =>
  Sem r o

-- |Send data to stdin.
send ::
   i o r .
  Member (Process i o) r =>
  i ->
  Sem r ()

-- |Create a scoped_ resource for 'Process'.
-- The process configuration may depend on the provided value of type @param@.
-- This variant models daemon processes that are expected to run forever, with 'Polysemy.Resume.Stop' being sent to this
-- function, if at all.
withProcess ::
   param i o r .
  Member (Scoped param (Process i o)) r =>
  param ->
  InterpreterFor (Process i o) r
withProcess :: forall param i o (r :: [(* -> *) -> * -> *]).
Member (Scoped param (Process i o)) r =>
param -> InterpreterFor (Process i o) r
withProcess =
  forall param (effect :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]).
Member (Scoped param effect) r =>
param -> InterpreterFor effect r
scoped @param

-- |Create a scoped_ resource for 'Process'.
-- The process configuration may depend on the provided value of type @param@.
-- This variant models processes that are expected to terminate, with 'Polysemy.Resume.Stop' being sent to individual
-- actions within the scope.
withProcessOneshot ::
   param i o err r .
  Member (Scoped param (Process i o !! err)) r =>
  param ->
  InterpreterFor (Process i o !! err) r
withProcessOneshot :: forall param i o err (r :: [(* -> *) -> * -> *]).
Member (Scoped param (Process i o !! err)) r =>
param -> InterpreterFor (Process i o !! err) r
withProcessOneshot =
  forall param (effect :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]).
Member (Scoped param effect) r =>
param -> InterpreterFor effect r
scoped @param

-- |Create a scoped_ resource for 'Process'.
-- The process configuration is provided to the interpreter statically.
-- This variant models daemon processes that are expected to run forever, with 'Polysemy.Resume.Stop' being sent to this
-- function, if at all.
withProcess_ ::
   i o r .
  Member (Scoped_ (Process i o)) r =>
  InterpreterFor (Process i o) r
withProcess_ :: forall i o (r :: [(* -> *) -> * -> *]).
Member (Scoped_ (Process i o)) r =>
InterpreterFor (Process i o) r
withProcess_ =
  Sem (Process i o : r) a -> Sem r a
forall (effect :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]).
Member (Scoped_ effect) r =>
InterpreterFor effect r
scoped_

-- |Create a scoped_ resource for 'Process'.
-- The process configuration is provided to the interpreter statically.
-- This variant models processes that are expected to terminate, with 'Polysemy.Resume.Stop' being sent to individual
-- actions within the scope.
withProcessOneshot_ ::
   i o err r .
  Member (Scoped_ (Process i o !! err)) r =>
  InterpreterFor (Process i o !! err) r
withProcessOneshot_ :: forall i o err (r :: [(* -> *) -> * -> *]).
Member (Scoped_ (Process i o !! err)) r =>
InterpreterFor (Process i o !! err) r
withProcessOneshot_ =
  Sem ((Process i o !! err) : r) a -> Sem r a
forall (effect :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]).
Member (Scoped_ effect) r =>
InterpreterFor effect r
scoped_

-- |Convert 'Output' and 'Input' to 'Process' for a daemon process.
runProcessIO ::
   i o r .
  Member (Process i o) r =>
  InterpretersFor [Output i, Input o] r
runProcessIO :: forall i o (r :: [(* -> *) -> * -> *]).
Member (Process i o) r =>
InterpretersFor '[Output i, Input o] r
runProcessIO =
  (forall (rInitial :: [(* -> *) -> * -> *]) x.
 Input o (Sem rInitial) x -> Sem r x)
-> Sem (Input o : r) a -> Sem r a
forall (e :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]) a.
FirstOrder e "interpret" =>
(forall (rInitial :: [(* -> *) -> * -> *]) x.
 e (Sem rInitial) x -> Sem r x)
-> Sem (e : r) a -> Sem r a
interpret \case
    Input o (Sem rInitial) x
Input ->
      forall i o (r :: [(* -> *) -> * -> *]).
Member (Process i o) r =>
Sem r o
recv @i @o
  (Sem (Input o : r) a -> Sem r a)
-> (Sem (Output i : Input o : r) a -> Sem (Input o : r) a)
-> Sem (Output i : Input o : r) a
-> Sem r a
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  (forall (rInitial :: [(* -> *) -> * -> *]) x.
 Output i (Sem rInitial) x -> Sem (Input o : r) x)
-> Sem (Output i : Input o : r) a -> Sem (Input o : r) a
forall (e :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]) a.
FirstOrder e "interpret" =>
(forall (rInitial :: [(* -> *) -> * -> *]) x.
 e (Sem rInitial) x -> Sem r x)
-> Sem (e : r) a -> Sem r a
interpret \case
    Output i
o ->
      forall i o (r :: [(* -> *) -> * -> *]).
Member (Process i o) r =>
i -> Sem r ()
send @i @o i
o

-- |Convert 'Output' and 'Input' to 'Process' for a oneshot process.
runProcessOneshotIO ::
   i o err r .
  Member (Process i o !! err) r =>
  InterpretersFor [Output i !! err, Input o !! err] r
runProcessOneshotIO :: forall i o err (r :: [(* -> *) -> * -> *]).
Member (Process i o !! err) r =>
InterpretersFor '[Output i !! err, Input o !! err] r
runProcessOneshotIO =
  (forall x (r0 :: [(* -> *) -> * -> *]).
 Input o (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Input o !! err) r
forall err (eff :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]).
FirstOrder eff "interpretResumable" =>
(forall x (r0 :: [(* -> *) -> * -> *]).
 eff (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Resumable err eff) r
interpretResumable \case
    Input o (Sem r0) x
Input ->
      forall err (eff :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]).
Members '[Resumable err eff, Stop err] r =>
InterpreterFor eff r
restop @err @(Process i o) (forall i o (r :: [(* -> *) -> * -> *]).
Member (Process i o) r =>
Sem r o
recv @i @o)
  (Sem ((Input o !! err) : r) a -> Sem r a)
-> (Sem (Resumable err (Output i) : (Input o !! err) : r) a
    -> Sem ((Input o !! err) : r) a)
-> Sem (Resumable err (Output i) : (Input o !! err) : r) a
-> Sem r a
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  (forall x (r0 :: [(* -> *) -> * -> *]).
 Output i (Sem r0) x -> Sem (Stop err : (Input o !! err) : r) x)
-> InterpreterFor (Resumable err (Output i)) ((Input o !! err) : r)
forall err (eff :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]).
FirstOrder eff "interpretResumable" =>
(forall x (r0 :: [(* -> *) -> * -> *]).
 eff (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Resumable err eff) r
interpretResumable \case
    Output i
o ->
      forall err (eff :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]).
Members '[Resumable err eff, Stop err] r =>
InterpreterFor eff r
restop @err @(Process i o) (forall i o (r :: [(* -> *) -> * -> *]).
Member (Process i o) r =>
i -> Sem r ()
send @i @o i
o)