{-# LANGUAGE UndecidableInstances,  OverlappingInstances, FlexibleInstances, TypeSynonymInstances
 #-}

module NIB.Pointfree where

import Data.Char
import Data.Maybe
import Data.List
import Control.Monad

-- \ Tuple functions

fst3 :: (a,b,c) -> a
fst3 (a,b,c) = a

snd3 :: (a,b,c) -> b
snd3 (a,b,c) = b

trd3 :: (a,b,c) -> c
trd3 (a,b,c) = c
trd (a,b,c) = c


-- | Lambdifies a function. See '(||*)' and '(&&*)' for uses of 'lambdify'.
-- | Used in order to make operators capable of operating on functions that later on
-- | are supplied some value that all functions operate on.
--
-- > (+*) = lambdify (+)
-- > fourTwo = (*4) +* (*2)
-- > 42 == fourTwo 7
lambdify :: (x -> y -> z) -> (t -> x) -> (t -> y) -> t -> z
lambdify f a b x = f (a x) (b x)

-- | Lambdifies '(||)'.
--
-- > isBlankOrCommaChecker = (==' ') ||* (==',')
-- > isBlankOrComma = isBlankOrCommaChecker 'j'
(||*) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
(||*) = lambdify (||)

-- | Lambdifies '(&&)'.
--
-- > isInRangeChecker = (>9) &&* (<30)
-- > isInRange = isInRangeChecker 17
(&&*) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
(&&*) = lambdify (&&)

-- | 2-point-free operator. Similar to '.', but where
-- | the second function takes two (2) arguments instead of one (1).
--
-- > multAndSquare (^2) .^.. (*)
-- > 36 == multAndSqare 2 3
(^..) :: (c -> d) -> (a -> b -> c) -> a -> b-> d
(f ^.. g) a = f . g a

-- | 3-point-free operator. See '(^..)'.
(^...) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
(f ^...g ) a b = f . g a b

-- | Split a 2-tuple 'x' into a 2-stack and pass it to 'f'.
-- | The same as uncurry.
(..%) :: (a -> b -> c) -> (a,b) -> c
(..%) = uncurry 

(..%..) :: (c->d->e) -> (a->b->(c,d)) -> a -> b -> e
(f ..%.. g) a b = f ..% g a b

-- | Split a 3-tuple 'x' into a 3-stack and pass it to 'f'.
(...%) :: (a -> b -> c -> d) -> (a,b,c) -> d
(...%) f x = f (fst3 x) (snd3 x) (trd3 x)


-- | Pipes a monadic return through a non-monadic transformation function.
-- | liftM with arguments flipped.
--
-- > readIO >>* toUpper
(>>*) :: Monad m => m a -> (a -> b) -> m b
(>>*) v f = liftM f v -- v >>= (return . f)

-- > f = ((+) 2, (*) 3)
-- > x = 7
-- > r = f ..@ x
-- 
-- | gives `(9, 21)`, i.e. `(2 + 9, 3 * 7)`.
(..@) :: (a -> b, a -> c) -> a -> (b,c) 
f ..@ x = (fst f x, snd f x)

-- | Same as `..@`, but with a 3-tuple. 
(...@) :: (a -> b, a -> c, a -> d) -> a -> (b,c,d)
f ...@ x = (fst3 f x, snd3 f x, trd3 f x)