{-# LANGUAGE DataKinds, PolyKinds, TypeFamilies, TypeInType, TypeOperators, UndecidableInstances #-} -- | Bifunctors. -- -- Bifunctors are "two-argument functors". -- -- This module is the type-level equivalent of "Data.Bifunctor". module Fcf.Class.Bifunctor ( Bimap , First , Second ) where import Fcf.Core (Exp, Eval) import Fcf.Combinators (Pure) -- $setup -- >>> import Fcf.Combinators (Flip) -- >>> import Fcf.Data.Nat (Nat, type (+), type (-)) -- >>> import Fcf.Data.Symbol (Symbol) -- | Type-level 'Data.Bifunctor.bimap'. -- -- >>> :kind! Eval (Bimap ((+) 1) (Flip (-) 1) '(2, 4)) -- Eval (Bimap ((+) 1) (Flip (-) 1) '(2, 4)) :: (Nat, Nat) -- = '(3, 3) data Bimap :: (a -> Exp a') -> (b -> Exp b') -> f a b -> Exp (f a' b') -- (,) type instance Eval (Bimap f g '(x, y)) = '(Eval (f x), Eval (g y)) -- Either type instance Eval (Bimap f g ('Left x)) = 'Left (Eval (f x)) type instance Eval (Bimap f g ('Right y)) = 'Right (Eval (g y)) -- | Type-level 'Data.Bifunctor.first'. -- Apply a function along the first parameter of a bifunctor. -- -- === __Example__ -- -- >>> :kind! Eval (First ((+) 1) '(3,"a")) -- Eval (First ((+) 1) '(3,"a")) :: (Nat, Symbol) -- = '(4, "a") data First :: (a -> Exp b) -> f a c -> Exp (f b c) type instance Eval (First f x) = Eval (Bimap f Pure x) -- | Type-level 'Data.Bifunctor.second'. -- Apply a function along the second parameter of a bifunctor. -- -- This is generally equivalent to 'Data.Functor.Map'. -- -- === __Example__ -- -- >>> :kind! Eval (Second ((+) 1) '("a",3)) -- Eval (Second ((+) 1) '("a",3)) :: (Symbol, Nat) -- = '("a", 4) data Second :: (c -> Exp d) -> f a c -> Exp (f a d) type instance Eval (Second g x) = Eval (Bimap Pure g x)