{- |
    Flow is a package that provides functions and operators for writing more
    understandable Haskell. It's an alternative to some common idioms like
    @($)@ for function application and @(.)@ for function composition.

    Flow is designed to be imported unqualified. It does not export anything
    that conflicts with
    <http://hackage.haskell.org/package/base the base package>.

    >>> import Flow

    For more information about Flow, please visit
    <http://taylor.fausak.me/flow/ the official site>.
-}
module Flow (
    -- * Function application
    apply, (|>), (<|),
    -- * Function composition
    compose, (.>), (<.),
    -- * Strict function application
    apply', (!>), (<!),
) where

import Prelude (seq)

{- $setup
    >>> import Prelude
    >>> let f = (+ 2)
    >>> let g = (* 2)
    >>> let h = (^ 2)
-}

{- |
    prop> apply x f == f x

    <https://en.wikipedia.org/wiki/Function_application Function application>.
    This is like the 'Prelude.$' operator.

    >>> apply False not
    True

    Using this function with many arguments is cumbersome. Use '|>' or '<|'
    instead.

    >>> False `apply` not `apply` fromEnum
    1

    This function usually isn't necessary since @'apply' x f@ is the same as
    @f x@. However it can come in handy when working with higher-order
    functions.

    >>> map (apply False) [not, id]
    [True,False]
-}
apply :: a -> (a -> b) -> b
apply x f = f x

{- |
    prop> (x |> f) == f x
    prop> (x |> f |> g) == g (f x)

    Left-associative 'apply' operator. This is like a flipped version of the
    'Prelude.$' operator. Read it as "apply forward" or "pipe into".

    >>> False |> not
    True

    Since this operator has such low precedence, it can be used to remove
    parentheses from complicated expressions.

    >>> False |> not |> fromEnum
    1

    This operator can be used with higher-order functions, but 'apply' might be
    clearer.

    >>> map (False |>) [not, id]
    [True,False]
-}
infixl 0 |>
(|>) :: a -> (a -> b) -> b
x |> f = apply x f

{- |
    prop> (f <| x) == f x
    prop> (g <| f <| x) == g (f x)

    Right-associative 'apply' operator. This is like the 'Prelude.$' operator.
    Read it as "apply backward" or "pipe from".

    >>> not <| False
    True

    This operator can be used to remove parentheses from complicated
    expressions because of its low precedence.

    >>> fromEnum <| not <| False
    1

    With higher-order functions, this operator is a clearer alternative to
    @flip 'apply'@.

    >>> map (<| False) [not, id]
    [True,False]
-}
infixr 0 <|
(<|) :: (a -> b) -> a -> b
f <| x = apply x f

{- |
    prop> compose f g x == g (f x)

    <https://en.wikipedia.org/wiki/Function_composition Function composition>.
    This is like the 'Prelude..' operator.

    >>> (compose not fromEnum) False
    1

    Composing many functions together quickly becomes unwieldy. Use '.>' or
    '<.' instead.

    >>> (not `compose` fromEnum `compose` succ) False
    2
-}
compose :: (a -> b) -> (b -> c) -> (a -> c)
compose f g = \ x -> g (f x)

{- |
    prop> (f .> g) x == g (f x)
    prop> (f .> g .> h) x == h (g (f x))

    Left-associative 'compose' operator. This is like a flipped version of the
    'Prelude..' operator. Read it as "compose forward" or "and then".

    >>> (not .> fromEnum) False
    1

    Thanks to its high precedence, composing many functions together is easy.

    >>> (not .> fromEnum .> succ) False
    2
-}
infixl 9 .>
(.>) :: (a -> b) -> (b -> c) -> (a -> c)
f .> g = compose f g

{- |
    prop> (g <. f) x == g (f x)
    prop> (h <. g <. f) x == h (g (f x))

    Right-associative 'compose' operator. This is like the 'Prelude..'
    operator. Read it as "compose backward" or "but first".

    >>> (fromEnum <. not) False
    1

    Composing many functions together is easy thanks to its high precedence.

    >>> (succ <. fromEnum <. not) False
    2
-}
infixr 9 <.
(<.) :: (b -> c) -> (a -> b) -> (a -> c)
g <. f = compose f g

{- |
    prop> apply' x f == seq x (f x)

    Strict function application. This is like the 'Prelude.$!' operator.

    >>> apply' undefined (const False)
    *** Exception: Prelude.undefined
-}
apply' :: a -> (a -> b) -> b
apply' x f = seq x (apply x f)

{- |
    prop> (x !> f) == seq x (f x)
    prop> (x !> f !> g) == seq x (g (seq x (f x)))

    Left-associative 'apply'' operator. This is like a flipped version of the
    'Prelude.$!' operator.

    >>> undefined !> const False
    *** Exception: Prelude.undefined
-}
infixl 0 !>
(!>) :: a -> (a -> b) -> b
x !> f = apply' x f

{- |
    prop> (f <! x) == seq x (f x)
    prop> (g <! f <! x) == seq x (g (seq x (f x)))

    Right-associative 'apply'' operator. This is like the 'Prelude.$!'
    operator.

    >>> const False <! undefined
    *** Exception: Prelude.undefined
-}
infixr 0 <!
(<!) :: (a -> b) -> a -> b
f <! x = apply' x f