{-| Module : Contravariant Description : Explore contravariant functors. Copyright : © Frank Jung, 2024 License : GPL-3.0-only From [Covariance, contravariance, and positive and negative position](https://www.schoolofhaskell.com/user/commercial/content/covariance-contravariance) and [What is a contravariant functor?](https://stackoverflow.com/questions/38034077/what-is-a-contravariant-functor) The implementation of the function-result `fmap` and the function-argument `contramap` are almost exactly the same thing: just function composition (the `.` operator). The only difference is on which side you compose the adaptor function @ fmap :: (r -> s) -> (a -> r) -> (a -> s) fmap adaptor f = adaptor . f fmap adaptor = (adaptor .) fmap = (.) contramap' :: (b -> a) -> (a -> r) -> (b -> r) contramap' adaptor f = f . adaptor contramap' adaptor = (. adaptor) contramap' = flip (.) @ Note that `contramap'` is not the same as `contramap` from `Data.Functor.Contravariant`. As you can't make a '-> r' an actual instance of Contravariant in Haskell code simply because the 'a' is not the last type parameter of ('->'). Conceptually it works perfectly well, and you can always use a newtype wrapper to swap the type parameters and make that an instance (the contravariant defines the the `Op` type for exactly this purpose). -} module Contravariant ( -- * Types MakeString (..) -- * Functions , plus3ShowInt , showInt ) where import Data.Functor.Contravariant (Contravariant (..)) -- | A contravariant functor. newtype MakeString a = MakeString { forall a. MakeString a -> a -> String makeString :: a -> String } -- | Map covariant functor over 'MakeString'. instance Contravariant MakeString where contramap :: forall a' a. (a' -> a) -> MakeString a -> MakeString a' contramap a' -> a f (MakeString a -> String g) = (a' -> String) -> MakeString a' forall a. (a -> String) -> MakeString a MakeString (a -> String g (a -> String) -> (a' -> a) -> a' -> String forall b c a. (b -> c) -> (a -> b) -> a -> c . a' -> a f) -- | Show an integer. showInt :: MakeString Int showInt :: MakeString Int showInt = (Int -> String) -> MakeString Int forall a. (a -> String) -> MakeString a MakeString Int -> String forall a. Show a => a -> String show -- | Show an integer plus 3. -- Compare this with the following code: -- @ -- plus3ShowInt = MakeString (show . (+ 3)) -- @ -- The `contramap` function is a more general way to compose the function. plus3ShowInt :: MakeString Int plus3ShowInt :: MakeString Int plus3ShowInt = (Int -> Int) -> MakeString Int -> MakeString Int forall a' a. (a' -> a) -> MakeString a -> MakeString a' forall (f :: * -> *) a' a. Contravariant f => (a' -> a) -> f a -> f a' contramap (Int -> Int -> Int forall a. Num a => a -> a -> a +Int 3) MakeString Int showInt -- How to deal wit newtypes: -- @ -- newtype Mark = MkMark Int deriving Show -- incMark (MkMark a) = MkMark (a + 1) -- x = MkMark 12 -- x :: Mark = MkMark 12 -- y = incMark x -- y :: Mark = MkMark 13 -- @