{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} module Data.Profunctor.Product.Examples where import qualified Data.Profunctor as P import qualified Data.Profunctor.Product as PP import qualified Data.Profunctor.Product.Default as D import Control.Applicative (Applicative, liftA2, pure, (<*>), ZipList(ZipList), getZipList) newtype Replicator r f a b = Replicator (r -> f b) deriving Functor instance Applicative f => D.Default (Replicator (f b) f) b b where def = Replicator id -- | A higher-order generalisation of 'Prelude.replicate'. For -- example -- -- @ -- foo :: IO (String, String, String) -- foo = replicateT getLine -- @ -- -- @ -- > foo -- Hello -- world -- ! -- (\"Hello\",\"world\",\"!\") -- @ replicateT :: D.Default (Replicator r f) b b => r -> f b replicateT = f where Replicator f = def' def' :: D.Default p a a => p a a def' = D.def -- Boilerplate that is derivable using generics but I never got round -- to implementing it. instance Applicative f => Applicative (Replicator r f a) where pure = Replicator . pure . pure Replicator f <*> Replicator x = Replicator (liftA2 (<*>) f x) instance Functor f => P.Profunctor (Replicator r f) where dimap _ h (Replicator f) = Replicator ((fmap . fmap) h f) instance Applicative f=> PP.ProductProfunctor (Replicator r f) where purePP = pure (****) = (<*>) -- In the real world this would be 'StateT [a] Maybe b' but I don't want to -- pick up the transformers dependency here newtype Take a z b = Take ([a] -> Maybe ([a], b)) deriving Functor instance D.Default (Take a) z a where def = Take (\as -> case as of [] -> Nothing (a:as') -> Just (as', a)) -- | A type safe generalisation of 'Prelude.take'. For example -- -- @ -- > let count = [1..] :: [Int] -- > takeT count :: Maybe (Int, Int) -- Just (1,2) -- > takeT count -- :: Maybe (Int, Int, (Int, (Int, Int), Int, Int), -- Const Int Bool, Identity (Int, Int), Tagged String Int) -- Just (1,2,(3,(4,5),6,7),Const 8,Identity (9,10),Tagged 11) -- @ takeT :: D.Default (Take a) b b => [a] -> Maybe b takeT = takeExplicit D.def where takeExplicit :: Take a b b -> [a] -> Maybe b takeExplicit (Take f) as = fmap snd (f as) -- More boilerplate instance Applicative (Take a z) where pure x = Take (\as -> pure (as, x)) Take f <*> Take x = Take (\as -> do (as', f') <- f as (as'', x') <- x as' return (as'', f' x')) instance P.Profunctor (Take a) where dimap _ g (Take h) = Take ((fmap . fmap . fmap) g h) instance PP.ProductProfunctor (Take a) where purePP = pure (****) = (<*>) newtype Traverse f a b = Traverse { runTraverse :: a -> f b } deriving Functor -- | Use 'sequenceT' instead. It has a better name. traverseT :: D.Default (Traverse f) a b => a -> f b traverseT = runTraverse D.def -- | Actually, @Sequence@ is a better name for this type Sequence = Traverse -- | A higher-order generalisation of 'Data.Traversable.sequenceA'. For example -- -- @ -- > sequenceT (print 3110, putStrLn "World") :: IO ((), ()) -- 3110 -- World -- ((),()) -- @ sequenceT :: D.Default (Sequence f) a b => a -> f b sequenceT = runTraverse D.def -- If we used this then inference may get better: -- -- instance a ~ b => D.Default (Traverse f) (f a) b where instance D.Default (Traverse f) (f a) a where def = Traverse id -- Boilerplate that is derivable using generics but I never got round -- to implementing it. instance Applicative f => Applicative (Traverse f a) where pure = Traverse . pure . pure Traverse f <*> Traverse x = Traverse (liftA2 (<*>) f x) instance Functor f => P.Profunctor (Traverse f) where dimap g h (Traverse f) = Traverse (P.dimap g (fmap h) f) instance Applicative f => PP.ProductProfunctor (Traverse f) where purePP = pure (****) = (<*>) newtype Zipper a b = Zipper { unZipper :: Traverse ZipList a b } deriving Functor instance a ~ b => D.Default Zipper [a] b where def = Zipper (P.dimap ZipList id D.def) -- { Boilerplate instance P.Profunctor Zipper where dimap f g = Zipper . P.dimap f g . unZipper instance Applicative (Zipper a) where pure = Zipper . pure f <*> x = Zipper ((<*>) (unZipper f) (unZipper x)) instance PP.ProductProfunctor Zipper where purePP = pure (****) = (<*>) -- } -- | A challenge from a Clojurist on Hacker News -- (https://news.ycombinator.com/item?id=23939350) -- -- @ -- > cl_map (uncurry (+)) ([1,2,3], [4,5,6]) -- [5,7,9] -- -- > cl_map (+3) [1,2,3] -- [4,5,6] -- -- > let max3 (x, y, z) = x `max` y `max` z -- > cl_map max3 ([1,20], [3,4], [5,6]) -- [5,20] -- @ cl_map :: D.Default Zipper a b => (b -> r) -> a -> [r] cl_map f = getZipList . fmap f . runTraverse (unZipper D.def)