Skip to content

Instantly share code, notes, and snippets.

@techtangents
Last active December 15, 2015 00:39
Show Gist options
  • Save techtangents/5174726 to your computer and use it in GitHub Desktop.
Save techtangents/5174726 to your computer and use it in GitHub Desktop.
{-
Recall that function composition is:
f . g = f (g x)
Consider, (f . g) as the application of g to an parameter destined for f.
Let's decribe g as the 'mapper' function and f as the 'primary function'
This gist generalises this idea to primary functions of different arities (in curried form).
Each parameter to the primary function has its own mapper function.
The API forms a similar style to that of Applicative, where infix functions chain values together in an expression.
In addition to the chaining, the explicit on1..5 may be used.
Basic usage:
Given a sequence of functions, chain the final two with (+->) then the ones at the start with (*->).
Then apply the primary function, then its arguments.
e.g.
(fa *-> fb +-> fc) f a b c
-}
import Control.Category
import Control.Applicative
-- for example below
import Data.Text
-- infix combinator api
infixr +->, *->
(+->) ::
Category cat =>
(a -> b')
-> cat a1 b
-> (b' -> cat b c)
-> a
-> cat a1 c
fa +-> fb =
fa *-> (<<< fb)
(*->) ::
Category cat =>
cat a b
-> cat c d
-> cat b c
-> cat a d
(*->) ab cd bc =
ab >>> bc >>> cd
-- special-case APIs
-- - not strictly necessary - mainly for demonstration of the above APIs
-- - the on1..n' might be useful where the same mapper is used over multiple arguments
-- - the type signatures below are more specific than necessary, as (->) generalises to category. TODO
on1 ::
(a -> a')
-> (a' -> z)
-> a
-> z
on1 fa fb =
fa >>> fb
on2 ::
(a -> a')
-> (b -> b')
-> (a' -> b' -> z)
-> a
-> b
-> z
on2 fa fb =
fa +-> fb
on2' ::
(a -> a')
-> (a' -> a' -> z)
-> a
-> a
-> z
on2' fa =
on2 fa fa
on3 ::
(a -> a')
-> (b -> b')
-> (c -> c')
-> (a' -> b' -> c' -> z)
-> a
-> b
-> c
-> z
on3 fa fb fc =
fa *-> fb +-> fc
on3' ::
(a -> a')
-> (a' -> a' -> a' -> z)
-> a
-> a
-> a
-> z
on3' fa =
on3 fa fa fa
on4 ::
(a -> a')
-> (b -> b')
-> (c -> c')
-> (d -> d')
-> (a' -> b' -> c' -> d' -> z)
-> a
-> b
-> c
-> d
-> z
on4 fa fb fc fd =
fa *-> fb *-> fc +-> fd
on4' ::
(a -> a')
-> (a' -> a' -> a' -> a' -> z)
-> a
-> a
-> a
-> a
-> z
on4' fa =
on4 fa fa fa fa
on5 ::
(a -> a')
-> (b -> b')
-> (c -> c')
-> (d -> d')
-> (e -> e')
-> (a' -> b' -> c' -> d' -> e' -> z)
-> a
-> b
-> c
-> d
-> e
-> z
on5 fa fb fc fd fe =
fa *-> fb *-> fc *-> fd +-> fe
on5' ::
(a -> a')
-> (a' -> a' -> a' -> a' -> a' -> z)
-> a
-> a
-> a
-> a
-> a
-> z
on5' fa =
on5 fa fa fa fa fa
-- My use case: adapt Data.Text.Replace to a String. How do we pointfree this?
-- Before:
replace' :: String -> String -> String -> String
replace' a b c = unpack (replace (pack a) (pack b) (pack c))
-- for starters, let's pointfree the outer function with fmap
fmap3 = (fmap <<< fmap <<< fmap)
replace'' :: String -> String -> String -> String
replace'' = fmap3 unpack (\a b c -> replace (pack a) (pack b) (pack c))
-- now we can pointfree the pack calls using the api above
replace''' :: String -> String -> String -> String
replace''' = fmap3 unpack (pack *-> pack +-> pack $ replace)
-- or the special-case apis:
replace'''' :: String -> String -> String -> String
replace'''' = fmap3 unpack (on3' pack replace)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment