content: {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE MonoLocalBinds #-}
  
  -- | This module contains several tiny examples of how to use effects.
  -- For technical details, see the documentation in the effect-modules.
  --
  -- Note that most examples given here are very small. For them,
  -- using `Eff` monad is more complicated compared to a standard functional
  -- approach.
  -- The power of extensible effects lie in the fact that these computations can
  -- be used to construct much more complicated programs by composing the little
  -- pieces shown here.
  --
  -- This module imports and reexports modules from this library and requires
  -- some language extensions:
  --
  -- @
  -- {-\# LANGUAGE ScopedTypeVariables \#-}
  -- {-\# LANGUAGE FlexibleContexts \#-}
  -- {-\# LANGUAGE MonoLocalBinds \#-}
  --
  -- import Control.Eff
  -- import Control.Eff.Reader.Lazy
  -- import Control.Eff.Writer.Lazy
  -- import Control.Eff.State.Lazy
  -- import Control.Eff.Exception
  -- @
  --
  -- If you want to see what each extension is good for, you can disable it and
  -- see what GHC will complain about.
  --
  module Control.Eff.QuickStart
    ( -- * Examples
      module Control.Eff.QuickStart
      -- * Imported effect modules
    , module Control.Eff.Reader.Lazy
    , module Control.Eff.State.Lazy
    , module Control.Eff.Exception
    ) where
  
  import           Control.Eff
  import           Control.Eff.Reader.Lazy
  import           Control.Eff.State.Lazy
  import           Control.Eff.Exception
  import           Control.Monad                            ( when )
  
  -- | an effectful function that can throw an error
  --
  -- @
  -- tooBig i = do
  --   when (i > 100) $ throwError $ show i
  --   return i
  -- @
  tooBig :: Member (Exc String) r => Int -> Eff r Int
  tooBig i = do
    when (i > 100) $ throwError $ show i
    return i
  
  -- | run the @tooBig@ effect based on a provided Int.
  --
  -- @
  -- runTooBig i = run . runError $ tooBig i
  -- @
  --
  -- >>> runTooBig 1
  -- Right 1
  --
  -- >>> runTooBig 200
  -- Left "200"
  runTooBig :: Int -> Either String Int
  runTooBig i = run . runError $ tooBig i
  
  -- | an effectul computation using state. The state is of type @[Int]@.
  -- This function takes the head off the list, if it is there and return it.
  -- If state is the empty list, then it stays the same and returns @Nothing@.
  --
  -- @
  -- popState = do
  --  stack <- get
  --  case stack of
  --    []       -> return Nothing
  --    (x : xs) -> do
  --      put xs
  --      return $ Just x
  -- @
  popState :: Member (State [Int]) r => Eff r (Maybe Int)
  popState = do
    stack <- get
    case stack of
      []       -> return Nothing
      (x : xs) -> do
        put xs
        return $ Just x
  
  -- | run the popState effectful computation based on initial state. The
  -- result-type is the result of the computation @Maybe Int@ together with the
  -- state at the end of the computation @[Int]@
  --
  -- @
  -- runPopState xs = run . runState xs $ popState
  -- @
  --
  -- >>> runPopState  [1, 2, 3]
  -- (Just 1,[2,3])
  --
  -- >>> runPopState []
  -- (Nothing,[])
  runPopState :: [Int] -> (Maybe Int, [Int])
  runPopState xs = run . runState xs $ popState
  
  -- | an effect that returns a number one more than the given
  --
  -- @
  -- oneMore = do
  --   x <- ask -- query the environment
  --   return $ x + 1 -- add one to the asked value and return it
  -- @
  oneMore :: Member (Reader Int) r => Eff r Int
  oneMore = do
    x <- ask
    return $ x + 1
  
  -- | Run the @oneMore@ effectful function by giving it a value to read.
  --
  -- @
  -- runOneMore i = run . runReader i $ oneMore
  -- @
  --
  -- >>> runOneMore 1
  -- 2
  runOneMore :: Int -> Int
  runOneMore i = run . runReader i $ oneMore
  
  -- | An effectful computation with multiple effects:
  --
  -- * A value gets read
  -- * an error can be thrown depending on the read value
  -- * state gets read and transformed
  --
  -- All these effects are composed using the @Eff@ monad using the corresponding
  -- Effect types.
  --
  -- @
  -- something = do
  --   readValue :: Float <- ask -- read a value from the environment
  --   when (readValue < 0) $ throwError readValue  -- if the value is negative, throw an error
  --   modify (\l -> (round readValue :: Integer) : l) -- add the rounded read element to the list
  --   currentState :: [Integer] <- get -- get the state after the modification
  --   return $ sum currentState -- sum the elements in the list and return that
  -- @
  something
    :: (Member (Reader Float) r, Member (State [Integer]) r, Member (Exc Float) r)
    => Eff r Integer
  something = do
    readValue :: Float <- ask
    when (readValue < 0) $ throwError readValue
    modify (\l -> (round readValue :: Integer) : l)
    currentState :: [Integer] <- get
    return $ sum currentState
  
  -- | Run the @someting@ effectful computation given in the previous function.
  -- The handlers apply from bottom to top - so this is the reading direction.
  --
  -- @
  -- runSomething1 initialState newValue =
  --   run . -- run the Eff-monad with no effects left
  --   runError . -- run the error part of the effect. This introduces the Either in the result.
  --   runState initialState . -- handle the state-effect providing an initial state giving back a pair.
  --   runReader newValue $ -- provide the computation with the dynamic value to read/ask for
  --   something -- the computation - function
  -- @
  --
  -- >>> runSomething1 [] (-0.5)
  -- Left (-0.5)
  --
  -- >>> runSomething1 [2] 1.3
  -- Right (3,[1,2])
  runSomething1 :: [Integer] -> Float -> Either Float (Integer, [Integer])
  runSomething1 initialState newValue =
    run . runError . runState initialState . runReader newValue $ something
  
  -- | Run the @something@ effectful computation given above.
  -- This has an alternative ordering of the effect-handlers.
  --
  -- The used effect-handlers are the same are used in slightly different order:
  -- The @runState@ and @runError@ methods are swapped, which results in a
  -- different output type and run-semantics.
  --
  -- @
  -- runSomething1 initialState newValue =
  --   run .
  --   runState initialState .
  --   runError .
  --   runReader newValue $
  --   something -- the computation - function
  -- @
  --
  -- >>> runSomething2 [4] (-2.4)
  -- (Left (-2.4),[4])
  --
  -- >>> runSomething2 [4] 5.9
  -- (Right 10,[6,4])
  runSomething2 :: [Integer] -> Float -> (Either Float Integer, [Integer])
  runSomething2 initialState newValue =
    run . runState initialState . runError . runReader newValue $ something
  