{-# LANGUAGE NoImplicitPrelude #-} -- | -- Module: $HEADER$ -- Description: Lazy function combinator "between" and its variations. -- Copyright: (c) 2013-2015, Peter Trško -- License: BSD3 -- -- Maintainer: peter.trsko@gmail.com -- Stability: experimental -- Portability: NoImplicitPrelude -- -- Implementation of lazy 'between' combinator and its variations. For -- introductory documentation see module "Data.Function.Between" and -- for strict versions import "Data.Function.Between.Strict" module. -- -- Prior to version 0.10.0.0 functions defined in this module were directly -- in "Data.Function.Between". -- -- /Module available since version 0.10.0.0./ module Data.Function.Between.Lazy ( -- * Between Function Combinator -- -- | Captures common pattern of @\\g -> (f '.' g '.' h)@ where @f@ and @h@ -- are fixed parameters. between , (~@~) , (~@@~) -- ** Derived Combinators -- -- | Combinators that either further parametrise @f@ or @g@ in -- @f '.' g '.' h@, or apply '~@~' more then once. , (^@~) , (~@@^) , (^@^) , (^@@^) , between2l , between3l -- ** Lifted Combinators -- -- | Combinators based on '~@~', '^@~', '^@^', and their flipped variants, -- that use 'fmap' to lift one or more of its arguments to operate in -- 'Functor' context. , (<~@~>) , (<~@@~>) , (<~@~) , (~@@~>) , (~@~>) , (<~@@~) , (<^@~) , (~@@^>) , (<^@^>) , (<^@@^>) , (<^@^) , (^@@^>) , (^@^>) , (<^@@^) -- * In-Between Function Application Combinator -- -- | Captures common pattern of @\\f -> (a \`f\` b)@ where @a@ and @b@ are -- fixed parameters. It doesn't look impressive untill one thinks about @a@ -- and @b@ as functions. -- -- /Since version 0.11.0.0./ , inbetween , (~$~) , (~$$~) , withIn , withReIn -- * Precursors to Iso, Lens and Prism -- -- | /Since version 0.11.0.0./ -- ** PreIso , PreIso , PreIso' , preIso , preIso' -- ** PreLens , PreLens , PreLens' , preLens , preLens' , preIsoToPreLens , le -- ** PrePrism , PrePrism , PrePrism' , prePrism , prePrism' ) where import Data.Either (Either(Left, Right)) import Data.Functor (Functor(fmap)) import Data.Function ((.), ($), const, flip, id) import Data.Maybe (Maybe, maybe) import Data.Function.Between.Types ( PreIso , PreIso' , PreLens , PreLens' , PrePrism , PrePrism' ) -- | Core combinator of this module and we build others on top of. It also has -- an infix form '~@~' and flipped infix form '~@@~'. -- -- This function Defined as: -- -- @ -- 'between' f g -> (f .) . (. g) -- @ between :: (c -> d) -> (a -> b) -> (b -> c) -> a -> d between f g = (f .) . (. g) -- | Infix variant of 'between'. -- -- Fixity is left associative and set to value 8, which is one less then fixity -- of function composition ('.'). (~@~) :: (c -> d) -> (a -> b) -> (b -> c) -> a -> d (~@~) = between infixl 8 ~@~ -- | Flipped variant of '~@~', i.e. flipped infix variant of 'between'. -- -- Fixity is right associative and set to value 8, which is one less then -- fixity of function composition ('.'). (~@@~) :: (a -> b) -> (c -> d) -> (b -> c) -> a -> d (~@@~) = flip between infixr 8 ~@@~ -- | As '~@~', but first function is also parametrised with @a@, hence the name -- '^@~'. Character @^@ indicates which argument is parametrised with -- additional argument. -- -- This function is defined as: -- -- @ -- (f '^@~' g) h a -> (f a '~@~' g) h a -- @ -- -- Fixity is left associative and set to value 8, which is one less then -- fixity of function composition ('.'). (^@~) :: (a -> c -> d) -> (a -> b) -> (b -> c) -> a -> d (f ^@~ g) h a = (f a `between` g) h a infixl 8 ^@~ -- | Flipped variant of '^@~'. -- -- Fixity is right associative and set to value 8, which is one less then -- fixity of function composition ('.'). (~@@^) :: (a -> b) -> (a -> c -> d) -> (b -> c) -> a -> d (~@@^) = flip (^@~) infixr 8 ~@@^ -- | Pass additional argument to first two function arguments. -- -- This function is defined as: -- -- @ -- (f '^@^' g) h a b -> (f a '~@~' g a) h b -- @ -- -- See also '^@~' to note the difference, most importantly that '^@~' passes -- the same argument to all its functional arguments. Function '^@~' can be -- defined in terms of this one as: -- -- @ -- (f '^@~' g) h a = (f '^@^' 'Data.Function.const' g) h a a -- @ -- -- We can do it also the other way around and define '^@^' using '^@~': -- -- @ -- f '^@^' g = -- 'Data.Tuple.curry' . (f . 'Data.Tuple.snd' '^@~' 'Data.Tuple.uncurry' g) -- @ -- -- Fixity is set to value 8, which is one less then of function composition -- ('.'). (^@^) :: (a -> d -> e) -> (a -> b -> c) -> (c -> d) -> a -> b -> e (f ^@^ g) h a = (f a `between` g a) h infix 8 ^@^ -- | Flipped variant of '^@^'. -- -- Fixity is set to value 8, which is one less then of function composition -- ('.'). (^@@^) :: (a -> b -> c) -> (a -> d -> e) -> (c -> d) -> a -> b -> e (^@@^) = flip (^@^) infix 8 ^@@^ -- | Apply function @g@ to each argument of binary function and @f@ to its -- result. In suffix \"2l\" the number is equal to arity of the function it -- accepts as a third argument and character \"l\" is for \"left associative\". -- -- @ -- 'between2l' f g = (f '~@~' g) '~@~' g -- @ -- -- Interesting observation: -- -- @ -- (\\f g -> 'between2l' 'Data.Function.id' g f) === 'Data.Function.on' -- @ between2l :: (c -> d) -> (a -> b) -> (b -> b -> c) -> a -> a -> d between2l f g = (f `between` g) `between` g -- | Apply function @g@ to each argument of ternary function and @f@ to its -- result. In suffix \"3l\" the number is equal to arity of the function it -- accepts as a third argument and character \"l\" is for \"left associative\". -- -- This function is defined as: -- -- @ -- 'between3l' f g = ((f '~@~' g) '~@~' g) '~@~' g -- @ -- -- Alternatively it can be defined using 'between2l': -- -- @ -- 'between3l' f g = 'between2l' f g '~@~' g -- @ between3l :: (c -> d) -> (a -> b) -> (b -> b -> b -> c) -> a -> a -> a -> d between3l f g = ((f `between` g) `between` g) `between` g -- | Convenience wrapper for: -- -- @ -- \\f g -> 'fmap' f '~@~' 'fmap' g -- @ -- -- Name of '<~@~>' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- both its arguments and then we apply '~@~'. -- -- Fixity is left associative and set to value 8, which is one less then -- of function composition ('.'). (<~@~>) :: (Functor f, Functor g) => (c -> d) -> (a -> b) -> (f b -> g c) -> f a -> g d f <~@~> g = fmap f `between` fmap g infix 8 <~@~> -- | Flipped variant of '<~@~>'. -- -- Name of '<~@@~>' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- both its arguments and then we apply '~@@~'. -- -- Fixity is set to value 8, which is one less then of function composition -- ('.'). (<~@@~>) :: (Functor f, Functor g) => (a -> b) -> (c -> d) -> (f b -> g c) -> f a -> g d g <~@@~> f = fmap f `between` fmap g infix 8 <~@@~> -- | Apply 'fmap' to first argument of '~@~'. Dual to '~@~>' which applies -- 'fmap' to second argument. -- -- Defined as: -- -- @ -- f '<~@~' g = 'fmap' f '~@~' g -- @ -- -- This function allows us to define lenses mostly for pair of functions that -- form an isomorphism. See section <#g:3 Constructing Lenses> for details. -- -- Name of '<~@~' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- first (left) argument and then we apply '~@~'. -- -- Fixity is left associative and set to value 8, which is one less then -- of function composition ('.'). (<~@~) :: Functor f => (c -> d) -> (a -> b) -> (b -> f c) -> a -> f d (<~@~) = between . fmap infixl 8 <~@~ -- | Flipped variant of '<~@~'. -- -- This function allows us to define lenses mostly for pair of functions that -- form an isomorphism. See section <#g:3 Constructing Lenses> for details. -- -- Name of '~@@~>' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- second (right) argument and then we apply '~@@~'. -- -- Fixity is right associative and set to value 8, which is one less then -- fixity of function composition ('.'). (~@@~>) :: Functor f => (a -> b) -> (c -> d) -> (b -> f c) -> a -> f d (~@@~>) = flip (<~@~) infixr 8 ~@@~> -- | Apply 'fmap' to second argument of '~@~'. Dual to '<~@~' which applies -- 'fmap' to first argument. -- -- Defined as: -- -- @ -- f '~@~>' g -> f '~@~' 'fmap' g -- @ -- -- Name of '~@~>' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- second (right) argument and then we apply '~@~'. -- -- Fixity is right associative and set to value 8, which is one less then -- of function composition ('.'). (~@~>) :: Functor f => (c -> d) -> (a -> b) -> (f b -> c) -> f a -> d (~@~>) f = between f . fmap infixl 8 ~@~> -- | Flipped variant of '~@~>'. -- -- Name of '<~@@~' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- first (left) argument and then we apply '~@@~'. -- -- Fixity is left associative and set to value 8, which is one less then -- fixity of function composition ('.'). (<~@@~) :: Functor f => (a -> b) -> (c -> d) -> (f b -> c) -> f a -> d (<~@@~) = flip (~@~>) infixr 8 <~@@~ -- | Convenience wrapper for: @\\f g -> 'fmap' . f '^@~' g@. -- -- This function has the same functionality as function -- -- @ -- lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b -- @ -- -- Which is defined in <http://hackage.haskell.org/package/lens lens package>. -- Only difference is that arguments of '<^@~' are flipped. See also section -- <#g:3 Constructing Lenses>. -- -- Name of '<^@~' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- first (left) arguments and then we apply '^@~'. -- -- Fixity is left associative and set to value 8, which is one less then -- of function composition ('.'). (<^@~) :: Functor f => (a -> c -> d) -> (a -> b) -> (b -> f c) -> a -> f d (<^@~) f = (fmap . f ^@~) infixl 8 <^@~ -- | Flipped variant of '~@^>'. -- -- This function has the same functionality as function -- -- @ -- lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b -- @ -- -- Which is defined in <http://hackage.haskell.org/package/lens lens package>. -- See also section <#g:3 Constructing Lenses>. -- -- Name of '~@^>' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- second (right) arguments and then we apply '~@^>'. -- -- Fixity is left associative and set to value 8, which is one less then -- of function composition ('.'). (~@@^>) :: Functor f => (a -> b) -> (a -> c -> d) -> (b -> f c) -> a -> f d (~@@^>) = flip (<^@~) infixl 8 ~@@^> -- | Convenience wrapper for: @\\f g -> 'fmap' . f '^@^' 'fmap' . g@. -- -- Name of '<^@^>' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- both its arguments and then we apply '^@^'. -- -- Fixity is left associative and set to value 8, which is one less then -- of function composition ('.'). (<^@^>) :: (Functor f, Functor g) => (a -> d -> e) -> (a -> b -> c) -> (f c -> g d) -> a -> f b -> g e (f <^@^> g) h a = (fmap (f a) `between` fmap (g a)) h infix 8 <^@^> -- | Flipped variant of '<^@^>'. -- -- Name of '<^@@^>' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- both its arguments and then we apply '^@@^'. -- -- Fixity is set to value 8, which is one less then of function composition -- ('.'). (<^@@^>) :: (Functor f, Functor g) => (a -> b -> c) -> (a -> d -> e) -> (f c -> g d) -> a -> f b -> g e (<^@@^>) = flip (<^@^>) infix 8 <^@@^> -- | Convenience wrapper for: @\\f g -> 'fmap' . f '^@^' g@. -- -- This function allows us to define generic lenses from gettern and setter. -- See section <#g:3 Constructing Lenses> for details. -- -- Name of '<^@^' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- first (left) arguments and then we apply '^@^'. -- -- Fixity is left associative and set to value 8, which is one less then -- of function composition ('.'). (<^@^) :: Functor f => (a -> d -> e) -> (a -> b -> c) -> (c -> f d) -> a -> b -> f e (f <^@^ g) h a = (fmap (f a) `between` g a) h infix 8 <^@^ -- | Flipped variant of '<^@^'. -- -- This function allows us to define generic lenses from gettern and setter. -- See section <#g:3 Constructing Lenses> for details. -- -- Name of '^@@^>' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- second (right) arguments and then we apply '^@@^'. -- -- Fixity is set to value 8, which is one less then of function composition -- ('.'). (^@@^>) :: Functor f => (a -> b -> c) -> (a -> d -> e) -> (c -> f d) -> a -> b -> f e (^@@^>) = flip (<^@^) infix 8 ^@@^> -- | Convenience wrapper for: @\\f g -> f '^@^' 'fmap' . g@. -- -- Name of '^@^>' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- second (right) arguments and then we apply '^@^'. -- -- Fixity is left associative and set to value 8, which is one less then -- of function composition ('.'). (^@^>) :: Functor f => (a -> d -> e) -> (a -> b -> c) -> (f c -> d) -> a -> f b -> e (f ^@^> g) h a = (f a `between` fmap (g a)) h infix 8 ^@^> -- | Flipped variant of '^@^>'. -- -- Name of '<^@@^' simply says that we apply 'Data.Functor.<$>' ('fmap') to -- first (left) arguments and then we apply '^@@^'. -- -- Fixity is set to value 8, which is one less then of function composition -- ('.'). (<^@@^) :: Functor f => (a -> b -> c) -> (a -> d -> e) -> (f c -> d) -> a -> f b -> e (<^@@^) = flip (^@^>) infix 8 <^@@^ -- {{{ In-Between Function Application Combinator ----------------------------- -- | Prefix version of common pattern: -- -- @ -- \\f -> a \`f\` b -- @ -- -- Where @a@ and @b@ are fixed parameters. There is also infix version named -- '~$~'. This function is defined as: -- -- @ -- 'inbetween' a b f = f a b -- @ -- -- Based on the above definition one can think of it as a variant function -- application that deals with two arguments, where in example -- 'Data.Function.$' only deals with one. -- -- /Since version 0.11.0.0./ inbetween :: a -> b -> (a -> b -> r) -> r inbetween a b f = f a b infix 8 `inbetween` -- | Infix version of common pattern: -- -- @ -- \\f -> a \`f\` b -- @ -- -- Where @a@ and @b@ are fixed parameters. There is also prefix version named -- 'inbetween'. -- -- /Since version 0.11.0.0./ (~$~) :: a -> b -> (a -> b -> r) -> r (~$~) = inbetween infix 8 ~$~ -- | Infix version of common pattern: -- -- @ -- \\f -> a \`f\` b -- Notice the order of \'a\' and \'b\'. -- @ -- -- /Since version 0.11.0.0./ (~$$~) :: b -> a -> (a -> b -> r) -> r (b ~$$~ a) f = f a b infix 8 ~$$~ -- | Construct a function that encodes idiom: -- -- @ -- \\f -> a \`f\` b -- Notice the order of \'b\' and \'a\'. -- @ -- -- Function 'inbetween' can be redefined in terms of 'withIn' as: -- -- @ -- a \``inbetween`\` b = 'withIn' 'Data.Function.$' \\f -> a \`f\` b -- @ -- -- On one hand you can think of this function as a specialized 'id' function -- and on the other as a function application 'Data.Function.$'. All the -- following definitions work: -- -- @ -- 'withIn' f g = f g -- 'withIn' = 'id' -- 'withIn' = ('Data.Function.$') -- @ -- -- Usage examples: -- -- @ -- newtype Foo a = Foo a -- -- inFoo :: ((a -> Foo a) -> (Foo t -> t) -> r) -> r -- inFoo = 'withIn' '$' \\f -> -- Foo \`f\` \\(Foo a) -> Foo -- @ -- -- @ -- data Coords2D = Coords2D {_x :: Int, _y :: Int} -- -- inX :: ((Int -> Coords2D -> Coords2D) -> (Coords2D -> Int) -> r) -> r -- inX = 'withIn' '$' \\f -> -- (\\b s -> s{_x = b}) \`f\` _x -- @ -- -- /Since version 0.11.0.0./ withIn :: ((a -> b -> r) -> r) -> (a -> b -> r) -> r withIn = id -- | Construct a function that encodes idiom: -- -- @ -- \\f -> b \`f\` a -- Notice the order of \'b\' and \'a\'. -- @ -- -- Function '~$$~' can be redefined in terms of 'withReIn' as: -- -- @ -- b '~$$~' a = 'withReIn' '$' \\f -> b \`f\` a -- @ -- -- As 'withIn', but the function is flipped before applied. All of the -- following definitions work: -- -- @ -- 'withReIn' f g = f ('flip' g) -- 'withReIn' = ('.' 'flip') -- @ -- -- Usage examples: -- -- @ -- newtype Foo a = Foo a -- -- inFoo :: ((a -> Foo a) -> (Foo t -> t) -> r) -> r -- inFoo = 'withReIn' '$' \\f -> -- (\\(Foo a) -> Foo) \`f\` Foo -- @ -- -- @ -- data Coords2D = Coords2D {_x :: Int, _y :: Int} -- -- inX :: ((Int -> Coords2D -> Coords2D) -> (Coords2D -> Int) -> r) -> r -- inX = 'withReIn' '$' \\f -> -- _x \`f\` \\b s -> s{_x = b} -- @ -- -- /Since version 0.11.0.0./ withReIn :: ((b -> a -> r) -> r) -> (a -> b -> r) -> r withReIn = (. flip) -- {{{ PreIso ----------------------------------------------------------------- -- | Construct a 'PreIso'; this function similar to /Iso/ constructor function -- from /lens/ package: -- -- @ -- iso :: (s -> a) -> (b -> t) -> Iso s t a b -- @ -- -- Usage example: -- -- @ -- data Foo a = Foo a -- -- preFoo :: 'PreIso' r (Foo a) (Foo b) a b -- preFoo = Foo \``preIso`\` \\(Foo a) -> a -- @ preIso :: (s -> a) -> (b -> t) -> PreIso r s t a b preIso = (~$$~) {-# INLINE preIso #-} -- | Flipped variant of 'preIso'. -- -- Usage example: -- -- @ -- data Foo a = Foo {_getFoo :: a} -- -- preFoo :: 'PreIso' r (Foo a) (Foo b) a b -- preFoo = _getFoo \``preIso'`\` Foo -- @ preIso' :: (b -> t) -> (s -> a) -> PreIso r s t a b preIso' = inbetween {-# INLINE preIso' #-} -- }}} PreIso ----------------------------------------------------------------- -- {{{ PreLens ---------------------------------------------------------------- -- | Construct a 'PreLens'; this function is similar to /Lens/ constructor -- function from /lens/ package: -- -- @ -- lens :: (s -> b -> t) -> (s -> a) -> Lens' s t a b -- @ -- -- Usage example: -- -- @ -- data Coords2D = Coords2D {_x :: Int, _y :: Int} -- -- preX :: PreLens' r Coords2D Int -- preX = (\\s b -> s{_x = b}) \``preLens`\` _x -- @ preLens :: (s -> b -> t) -> (s -> a) -> PreLens r s t a b preLens setter getter = flip setter ~$~ getter {-# INLINE preLens #-} -- | Flipped version of 'preLens' that takes getter first and setter second. -- -- @ -- data Coords2D = Coords2D {_x :: Int, _y :: Int} -- -- preX :: PreLens' r Coords2D Int -- preX = _x \``preLens'`\` \\s b -> s{_x = b} -- @ preLens' :: (s -> a) -> (s -> b -> t) -> PreLens r s t a b preLens' = flip preLens {-# INLINE preLens' #-} -- | Construct a @Lens@ out of a 'PreLens'. -- -- @ -- data Coords2D = Coords2D {_x :: Int, _y :: Int} -- -- preX :: PreLens' r Coords2D Int -- preX = _x \``preLens'`\` \\s b -> s{_x = b} -- -- x :: Lens' Coords2D Int -- x = 'le' preX -- @ le :: Functor f => PreLens ((a -> f b) -> s -> f t) s t a b -> (a -> f b) -> s -> f t le = ($ ((<^@~) . flip)) {-# INLINE le #-} -- }}} PreLens ---------------------------------------------------------------- -- {{{ PrePrism --------------------------------------------------------------- -- | Constract a 'PrePrism'; this function is similar to /Prism/ constructor -- function from /lens/ package: -- -- @ -- prism :: (b -> t) -> (s -> 'Either' t a) -> Prism s t a b -- @ -- -- Usage example: -- -- @ -- {-\# LANGUAGE LambdaCase \#-} -- data Sum a b = A a | B b -- -- preA :: 'PrePrism' r (Sum a c) (Sum b c) a b -- preA = 'prePrism' A '$' \\case -- A a -> 'Right' a -- B b -> 'Left' (B b) -- @ prePrism :: (b -> t) -> (s -> Either t a) -> PrePrism r s t a b prePrism = inbetween {-# INLINE prePrism #-} -- | Simplified construction of 'PrePrism', which can be used in following -- situations: -- -- * Constructing /Prism/ for types isomorphic to 'Maybe' or -- -- * when using 'Data.Typeable.cast' operation, or similar, which either -- returns what you want or 'Data.Maybe.Nothing'. -- -- Alternative type signature of this function is also: -- -- @ -- 'prePrism'' :: 'PreIso' r s s ('Maybe' a) b -> 'PrePrism' r s s a b -- @ prePrism' :: (b -> s) -> (s -> Maybe a) -> PrePrism r s s a b prePrism' ana cata = ana ~$~ \s -> maybe (Left s) Right (cata s) {-# INLINE prePrism' #-} -- | Convert 'PreIso' in to 'PreLens' by injecting const to a setter function. -- -- @ -- 'preIsoToPreLens' aPreIso f = aPreIso '$' \\fbt fsa -> 'const' fbt \`f\` fsa -- @ preIsoToPreLens :: PreIso r s t a b -> PreLens r s t a b preIsoToPreLens aPreIso f = aPreIso $ \fbt fsa -> (const . fbt) `f` fsa {-# INLINE preIsoToPreLens #-} -- }}} PrePrism --------------------------------------------------------------- -- }}} In-Between Function Application Combinator -----------------------------