```﻿{-| Map the arguments and return value of functions.

General use:

- @f \$* g1 \$\$ g2 … \$\$ gn *\$ h = \\x1 … xn -> h (f (g1 x1) (g2 x2) … (gn xn))@

Examples:

- @on f g = f \$* g \$\$ g *\$ id@
- @comparing f = compare \$* f \$\$ f *\$ id@
- @f . g = f \$* g *\$ id@

Related work:

- <http://conal.net/blog/posts/semantic-editor-combinators Semantic Editor Combinators>
(Conal Elliott, 2008\/11\/24). Introduces composable editors for
function arguments and return values.
- <http://matt.immute.net/content/pointless-fun Pointless Fun> (Matt
Hellige, 2008\/12\/03). Derives a similar operator named @~>@. This
operator composes an editor which can then be applied to the subject
to edit. This better agrees with Conal's concept of Semantic Editor
Combinators. In contrast, @\$*@ includes the subject as part of the
editor, though you can write @(\$* g1 \$\$ g2 … \$\$ gn *\$ h)@ for an
editor independent of the subject. Also, @~>@ is right-associative
whereas @\$*@ and @\$\$@ are left-associative.
(Conal Elliott, 2016\/01\/15). Package including an implementation of
@~>@.
-}
module Data.Function.Meld
( (\$*)
, (\$\$)
, (*\$)
) where

import Prelude (flip)
import Control.Category (Category, (.), id)

-- | Map a function argument.
arg :: Category cat => cat a' a -> cat a b -> cat a' b
arg = flip (.)

-- | Map a function return value.
ret :: Category cat => cat b c -> cat a b -> cat a c
ret = (.)

-- | Begin melding.
--
-- @f \$* g1 \$\$ g2 … \$\$ gn *\$ h = \\x1 … xn -> h (f (g1 x1) (g2 x2) … (gn xn))@
(\$*) :: Category cat =>
cat b c
-> cat a b
-> cat c d
-> cat a d
(\$*) x f g = ret g (arg f x)
infixl 8 \$*

-- | Continue melding.
--
-- @f \$* g1 \$\$ g2 … \$\$ gn *\$ h = \\x1 … xn -> h (f (g1 x1) (g2 x2) … (gn xn))@
(\$\$) :: Category cat =>
((cat b c -> cat a d) -> e)
-> cat a b
-> cat c d
-> e
(\$\$) f g h = f (ret h . arg g)
infixl 7 \$\$

-- | Finish melding.
--
-- @f \$* g1 \$\$ g2 … \$\$ gn *\$ h = \\x1 … xn -> h (f (g1 x1) (g2 x2) … (gn xn))@
(*\$) :: Category cat => cat a b -> cat a b
(*\$) = id
infixl 6 *\$
```