-- |Helper functions for functors.
--
--  These operators are designed to make the interoperation between monadic
--  and pure computations more convenient by allowing them to be chained together
--  without peppering the program with superflouos return statements.
--
--  Each function is a pure analogue of a monadic one. The correspondences are
--  as follows:
--
--  * @>$>   ~  >>=@ (bind)
--  * @$>    ~  >> @ (throw away left argument)
--  * @<$    ~  << @   (re-exported from "Data.Functor")
--  * @<$<   ~  =<< @  (same as 'Data.Functor.<$>', but with the precedence of '>>=')
--  * @>=$>  ~  >=> @ (Kleisli composition)
--  * @<$=<  ~  <=< @ (flipped Kleisli composition)
--
-- Lastly, '|>' is left-to-right function composition (flipped version of '$').
module Data.Functor.Monadic (
   module Data.Functor,
   (>$>),
   ($>),
   (<$<),
   (>=$>),
   (<$=<),
   (|>)) where

import Data.Functor ((<$))

infixl 1 >$>
infixl 1 $>
infixr 1 <$<
infixl 1 >=$>
infixr 1 <$=<
infixl 1 |>

-- |Flipped 'fmap' for chaining plain functions after a functor in the following
--  way:
--
-- @
-- readFile '1.txt' >$> lines >$> map length >>= print
-- @
--
-- @lines@ and @map length@ are non-monadic functions, but peppering
-- them with returns, as pure '>>=' necessitates, is quite tedious.
--
-- In general:
--
-- @
-- m >>= return . f   is the same as   m >$> f
-- @
(>$>) :: Functor f => f a -> (a -> b) -> f b
(>$>) = flip fmap

-- |Left-associatiative, flipped '$>'. Corresponds to '>>'
($>) :: Functor f => f b -> a -> f a
($>) = flip (<$)

-- |Right-associative infix synonym for 'fmap'.
(<$<) :: Functor f => (a -> b) -> f a -> f b
(<$<) = fmap

-- |Application of '>$>' to Kleisli composition 'Control.Monad.>=>'
--  Use is analogous to that of '>$>', e.g.
--
--  @
--  f :: FilePath -> IO ()
--  f = (readFile >=$> lines >=$> map length >=> print)
--  @
--
--  In general:
--
--  @
--  m >=$> f   is the same as   m >=> return . f
--  @
(>=$>) :: Functor f => (a -> f b) -> (b -> c) -> a -> f c
(>=$>) f g x = f x >$> g

-- |Flipped version of '>=$>'.
(<$=<) :: Functor f => (b -> c) -> (a -> f b) -> a -> f c
(<$=<) = flip (>=$>)

-- |Flipped version of '$'.
(|>) :: a -> (a -> b) -> b
(|>) = flip ($)