packages feed

unfoldable 0.5.0 → 0.6.0

raw patch · 5 files changed

+352/−44 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Unfolder: packBFS :: f x -> BFS f x
- Data.Unfolder: runBFS :: Unfolder f => BFS f x -> f x
+ Data.Biunfoldable: arbitraryDefault :: (Arbitrary a, Arbitrary b, Biunfoldable t) => Gen (t a b)
+ Data.Biunfoldable: biunfold :: (Biunfoldable t, Unfolder f) => f a -> f b -> f (t a b)
+ Data.Biunfoldable: biunfoldBF :: (Biunfoldable t, Unfolder f) => f a -> f b -> f (t a b)
+ Data.Biunfoldable: biunfoldBF_ :: (Biunfoldable t, Unfolder f) => f (t () ())
+ Data.Biunfoldable: biunfold_ :: (Biunfoldable t, Unfolder f) => f (t () ())
+ Data.Biunfoldable: biunfoldr :: Biunfoldable t => (c -> Maybe (a, c)) -> (c -> Maybe (b, c)) -> c -> Maybe (t a b)
+ Data.Biunfoldable: class Biunfoldable t
+ Data.Biunfoldable: fromLists :: Biunfoldable t => [a] -> [b] -> Maybe (t a b)
+ Data.Biunfoldable: instance Biunfoldable (,)
+ Data.Biunfoldable: instance Biunfoldable Constant
+ Data.Biunfoldable: instance Biunfoldable Either
+ Data.Biunfoldable: randomDefault :: (Random a, Random b, RandomGen g, Biunfoldable t) => g -> (t a b, g)
+ Data.Unfoldable: allToDepth :: Unfoldable t => Int -> [t ()]
+ Data.Unfolder: NT :: (forall a. f a -> g a) -> NT f g
+ Data.Unfolder: NumConst :: a -> NumConst a x
+ Data.Unfolder: WithRec :: ReaderT (Int -> NT f f) f a -> WithRec f a
+ Data.Unfolder: ala :: (UnfolderTransformer t, Unfolder f) => (t f b -> f b) -> (t f a -> t f b) -> f a -> f b
+ Data.Unfolder: ala2 :: (UnfolderTransformer t, Unfolder f) => (t f c -> f c) -> (t f a -> t f b -> t f c) -> f a -> f b -> f c
+ Data.Unfolder: bfs :: Unfolder f => BFS f x -> f x
+ Data.Unfolder: class UnfolderTransformer t
+ Data.Unfolder: data NT f g
+ Data.Unfolder: getNT :: NT f g -> forall a. f a -> g a
+ Data.Unfolder: getNumConst :: NumConst a x -> a
+ Data.Unfolder: getWithRec :: WithRec f a -> ReaderT (Int -> NT f f) f a
+ Data.Unfolder: instance Alternative f => Alternative (WithRec f)
+ Data.Unfolder: instance Applicative f => Applicative (WithRec f)
+ Data.Unfolder: instance Eq (f a) => Eq (DualA f a)
+ Data.Unfolder: instance Eq a => Eq (NumConst a x)
+ Data.Unfolder: instance Functor (NumConst a)
+ Data.Unfolder: instance Functor f => Functor (WithRec f)
+ Data.Unfolder: instance Num a => Alternative (NumConst a)
+ Data.Unfolder: instance Num a => Applicative (NumConst a)
+ Data.Unfolder: instance Num a => Unfolder (NumConst a)
+ Data.Unfolder: instance Show (f a) => Show (DualA f a)
+ Data.Unfolder: instance Show a => Show (NumConst a x)
+ Data.Unfolder: instance Unfolder f => Unfolder (WithRec f)
+ Data.Unfolder: instance UnfolderTransformer BFS
+ Data.Unfolder: instance UnfolderTransformer DualA
+ Data.Unfolder: instance UnfolderTransformer WithRec
+ Data.Unfolder: lift :: (UnfolderTransformer t, Unfolder f) => f a -> t f a
+ Data.Unfolder: limitDepth :: Unfolder f => Int -> WithRec f a -> f a
+ Data.Unfolder: newtype NumConst a x
+ Data.Unfolder: newtype WithRec f a
+ Data.Unfolder: withRec :: (Int -> NT f f) -> WithRec f a -> f a

Files

+ examples/redblack.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UndecidableInstances #-}++import Control.Applicative+import Data.Unfoldable+import Data.Unfolder++import Data.Maybe+import System.Random+import Data.List (intercalate)++-- red-black tree implementation adapted from https://gist.github.com/2660297+data Nat = Zero | Succ Nat deriving (Eq, Ord, Show)+data NatW :: Nat -> * where+  ZeroW :: NatW Zero+  SuccW :: NatW n -> NatW (Succ n)++data RedBlack = Black | Red deriving (Eq, Ord, Show)++data RedBlackTree a where +  T :: Node Black n a -> RedBlackTree a+deriving instance Show a => Show (RedBlackTree a)++data Node :: RedBlack -> Nat -> * -> * where+  Leaf :: Node Black Zero a+  B :: Node cL    n a -> a -> Node cR    n a -> Node Black (Succ n) a+  R :: Node Black n a -> a -> Node Black n a -> Node Red    n       a+deriving instance Show a => Show (Node c n a)++instance Unfoldable RedBlackTree where+  unfold = u ZeroW+    where+      u :: forall n f a. (Unfoldable (Node Black n), Unfolder f) +        => NatW n -> f a -> f (RedBlackTree a)+      u n fa = choose +        [ T <$> (unfold :: Unfolder f => f a -> f (Node Black n a)) fa+        , u (SuccW n) fa+        ]+  +instance Unfoldable (Node Black Zero) where+  unfold _ = choose [ pure Leaf ]++instance Unfoldable (Node Black n) => Unfoldable (Node Black (Succ n)) where+  unfold = u+    where+      u :: forall f a. Unfolder f => f a -> f (Node Black (Succ n) a)+      u fa = choose +        [ B <$> b <*> fa <*> b+        , B <$> b <*> fa <*> r+        , B <$> r <*> fa <*> b+        , B <$> r <*> fa <*> r+        ]+        where+          r :: Unfolder f => f (Node Red n a)+          r = unfold fa+          b :: Unfolder f => f (Node Black n a)+          b = unfold fa++instance Unfoldable (Node Black n) => Unfoldable (Node Red n) where+  unfold fa = choose [ R <$> unfold fa <*> fa <*> unfold fa ]++rbtree :: Int -> RedBlackTree Int+rbtree l = fromJust $ fromList [0..l]++rbtreeShapes :: Int -> [RedBlackTree ()]+rbtreeShapes d = limitDepth d unfold_++randomRBTree :: IO (RedBlackTree Bool)+randomRBTree = getStdRandom randomDefault++newtype Formula = Formula [Integer]+instance Show Formula where+  show (Formula xs) +    | all (== 0) xs = "0"+    | otherwise = intercalate " + " $ map showOne $ filter ((/=0) . fst) $ zip xs [0..]+        where+          showOne (x, 0) = show x+          showOne (1, 1) = "x"+          showOne (x, 1) = show x ++ "x"+          showOne (1, n) = "x^" ++ show n+          showOne (x, n) = show x ++ "x^" ++ show n++add, mul :: [Integer] -> [Integer] -> [Integer]+add xs [] = xs+add [] ys = ys+add (x:xs) (y:ys) = (x + y) : add xs ys+mul _ [] = []+mul [] _ = []+mul (x:xs) (y:ys) = (x * y) : add (map (x*) ys) (add (map (y*) xs) (0 : mul xs ys))++instance Num Formula where+  fromInteger x = Formula [x]+  Formula xs + Formula ys = Formula (add xs ys)+  Formula xs * Formula ys = Formula (mul xs ys)++x :: Formula+x = Formula [0, 1]++-- See http://oeis.org/A001137+rbFormula :: Int -> NumConst Formula (RedBlackTree a)+rbFormula d = ala (limitDepth d) unfold (NumConst x)
+ src/Data/Biunfoldable.hs view
@@ -0,0 +1,112 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Data.Biunfoldable+-- Copyright   :  (c) Sjoerd Visscher 2012+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  sjoerd@w3future.com+-- Stability   :  experimental+-- Portability :  non-portable+--+-- Class of data structures with 2 type arguments that can be unfolded.+-----------------------------------------------------------------------------+module Data.Biunfoldable +  (++  -- * Biunfoldable+    Biunfoldable(..)+  , biunfold_+  , biunfoldBF+  , biunfoldBF_++  -- ** Specific unfolds+  , biunfoldr+  , fromLists+  , randomDefault+  , arbitraryDefault++  ) +  where++import Control.Applicative+import Data.Unfolder+import Data.Functor.Constant+import Control.Monad.Trans.State+import qualified System.Random as R+import Test.QuickCheck.Arbitrary (Arbitrary(..))+import Test.QuickCheck.Gen (Gen(..))+import Data.Maybe++-- | Data structures with 2 type arguments (kind @* -> * -> *@) that can be unfolded.+--+-- For example, given a data type+--+-- > data Tree a b = Empty | Leaf a | Node (Tree a b) b (Tree a b)+--+-- a suitable instance would be+--+-- > instance Biunfoldable Tree where+-- >   biunfold fa fb = choose+-- >     [ pure Empty+-- >     , Leaf <$> fa+-- >     , Node <$> biunfold fa fb <*> fb <*> biunfold fa fb+-- >     ]+--+-- i.e. it follows closely the instance for 'Bitraversable', but instead of matching on an input value,+-- we 'choose' from a list of all cases.+class Biunfoldable t where+  -- | Given a way to generate elements, return a way to generate structures containing those elements.+  biunfold :: Unfolder f => f a -> f b -> f (t a b)++-- | Unfold the structure, always using @()@ as elements.+biunfold_ :: (Biunfoldable t, Unfolder f) => f (t () ())+biunfold_ = biunfold (pure ()) (pure ())++-- | Breadth-first unfold, which orders the result by the number of 'choose' calls.+biunfoldBF :: (Biunfoldable t, Unfolder f) => f a -> f b -> f (t a b)+biunfoldBF = ala2 bfs biunfold++-- | Unfold the structure breadth-first, always using @()@ as elements.+biunfoldBF_ :: (Biunfoldable t, Unfolder f) => f (t () ())+biunfoldBF_ = bfs biunfold_++-- | @biunfoldr@ builds a data structure from a seed value.+biunfoldr :: Biunfoldable t => (c -> Maybe (a, c)) -> (c -> Maybe (b, c)) -> c -> Maybe (t a b)+biunfoldr fa fb z = terminate . flip runStateT z $ biunfoldBF (StateT $ maybeToList . fa) (StateT $ maybeToList . fb)+  where+    terminate [] = Nothing+    terminate ((t, c):ts) = if isNothing (fa c) && isNothing (fb c) then Just t else terminate ts++-- | Create a data structure using the lists as input.+-- This can fail because there might not be a data structure with the same number+-- of element positions as the number of elements in the lists.+fromLists :: Biunfoldable t => [a] -> [b] -> Maybe (t a b)+fromLists = curry $ biunfoldr unconsA unconsB+  where+    unconsA ([], _) = Nothing+    unconsA (a:as, bs) = Just (a, (as, bs))+    unconsB (_, []) = Nothing+    unconsB (as, b:bs) = Just (b, (as, bs))++-- | Generate a random value, can be used as default instance for 'R.Random'.+randomDefault :: (R.Random a, R.Random b, R.RandomGen g, Biunfoldable t) => g -> (t a b, g)+randomDefault = runState . getRandom $ biunfold (Random . state $ R.random) (Random . state $ R.random)++-- | Provides a QuickCheck generator, can be used as default instance for 'Arbitrary'.+arbitraryDefault :: (Arbitrary a, Arbitrary b, Biunfoldable t) => Gen (t a b)+arbitraryDefault = MkGen $ \r n -> let Arb _ f = biunfold arbUnit arbUnit in +  fromMaybe (error "Failed to generate a value.") (f r (n + 1))++instance Biunfoldable Either where+  biunfold fa fb = choose +    [ Left <$> fa+    , Right <$> fb+    ]++instance Biunfoldable (,) where+  biunfold fa fb = choose +    [ (,) <$> fa <*> fb ]++instance Biunfoldable Constant where+  biunfold fa _ = choose +    [ Constant <$> fa ]
src/Data/Unfoldable.hs view
@@ -25,6 +25,7 @@   , leftMost   , rightMost   , allDepthFirst+  , allToDepth   , allBreadthFirst   , randomDefault   , arbitraryDefault@@ -72,11 +73,11 @@  -- | Breadth-first unfold, which orders the result by the number of 'choose' calls. unfoldBF :: (Unfoldable t, Unfolder f) => f a -> f (t a)-unfoldBF = runBFS . unfold . packBFS+unfoldBF = ala bfs unfold  -- | Unfold the structure breadth-first, always using @()@ as elements. unfoldBF_ :: (Unfoldable t, Unfolder f) => f (t ())-unfoldBF_ = unfoldBF (pure ())+unfoldBF_ = bfs unfold_  -- | @unfoldr@ builds a data structure from a seed value. It can be specified as: -- @@ -104,11 +105,15 @@ rightMost :: Unfoldable t => Maybe (t ()) rightMost = getDualA unfold_ --- | Generate all the values depth first.+-- | Generate all the values depth-first. allDepthFirst :: Unfoldable t => [t ()] allDepthFirst = unfold_ --- | Generate all the values breadth first.+-- | Generate all the values upto a given depth, depth-first.+allToDepth :: Unfoldable t => Int -> [t ()]+allToDepth d = limitDepth d unfold_++-- | Generate all the values breadth-first. allBreadthFirst :: Unfoldable t => [t ()] allBreadthFirst = unfoldBF_ @@ -122,37 +127,43 @@   fromMaybe (error "Failed to generate a value.") (f r (n + 1))  instance Unfoldable [] where-  unfold f = choose +  unfold fa = choose      [ pure []-    , (:) <$> f <*> unfold f+    , (:) <$> fa <*> unfold fa     ]  instance Unfoldable Maybe where-  unfold f = choose +  unfold fa = choose      [ pure Nothing-    , Just <$> f+    , Just <$> fa     ]  instance (Bounded a, Enum a) => Unfoldable (Either a) where-  unfold f = choose +  unfold fa = choose      [ Left <$> boundedEnum-    , Right <$> f+    , Right <$> fa     ]  instance (Bounded a, Enum a) => Unfoldable ((,) a) where-  unfold f = (,) <$> boundedEnum <*> f+  unfold fa = choose +    [ (,) <$> boundedEnum <*> fa ]  instance Unfoldable Identity where-  unfold = fmap Identity+  unfold fa = choose +    [ Identity <$> fa ]  instance (Bounded a, Enum a) => Unfoldable (Constant a) where-  unfold = fmap Constant . const boundedEnum+  unfold _ = choose +    [ Constant <$> boundedEnum ]    instance (Unfoldable p, Unfoldable q) => Unfoldable (Product p q) where-  unfold f = Pair <$> unfold f <*> unfold f+  unfold fa = choose+    [ Pair <$> unfold fa <*> unfold fa ]  instance (Unfoldable p, Unfoldable q) => Unfoldable (Compose p q) where-  unfold = fmap Compose . unfold . unfold+  unfold fa = choose+    [ Compose <$> unfold (unfold fa) ]  instance Unfoldable f => Unfoldable (Reverse f) where-  unfold = fmap Reverse . getDualA . unfold . DualA+  unfold fa = choose+    [ Reverse <$> getDualA (unfold (DualA fa)) ]
src/Data/Unfolder.hs view
@@ -16,6 +16,7 @@ {-# LANGUAGE      ScopedTypeVariables   , GeneralizedNewtypeDeriving+  , RankNTypes   #-} module Data.Unfolder    (@@ -27,16 +28,28 @@   , boundedEnum      -- ** Unfolder instances-  , DualA(..)   , Random(..) -  , BFS(..)-  , runBFS-  , packBFS-     , Arb(..)   , arbUnit   +  , NumConst(..)+  +  -- * UnfolderTransformer+  , UnfolderTransformer(..)+  , ala+  , ala2+  +  -- ** UnfolderTransformer instances+  , DualA(..)++  , NT(..)+  , WithRec(..)+  , withRec+  , limitDepth+  +  , BFS(..)+  , bfs   )    where  @@ -61,7 +74,7 @@ import Test.QuickCheck.Arbitrary (Arbitrary(..)) import Test.QuickCheck.Gen (Gen(..)) -import Data.Monoid (Monoid)+import Data.Monoid (Monoid(..)) import Data.Maybe (catMaybes, listToMaybe) import Data.Foldable (asum) import Data.Traversable (traverse)@@ -83,6 +96,7 @@ chooseMonadDefault ms = chooseInt (length ms) >>= (ms !!)  -- | If a datatype is bounded and enumerable, we can use 'chooseInt' to generate a value.+-- This is the function to use if you want to unfold a datatype that has no type arguments (has kind *). boundedEnum :: forall f a. (Unfolder f, Bounded a, Enum a) => f a boundedEnum = (\x -> toEnum (x + lb)) <$> chooseInt (1 + ub - lb)   where@@ -107,26 +121,12 @@   chooseInt 0 = Nothing   chooseInt _ = Just 0 --- | 'DualA' flips the '(<|>)' operator.-newtype DualA f a = DualA { getDualA :: f a }-  deriving (Functor, Applicative)-instance Alternative f => Alternative (DualA f) where-  empty = DualA empty-  DualA a <|> DualA b = DualA (b <|> a)--- | Reverse the list passed to choose.-instance Unfolder f => Unfolder (DualA f) where-  choose = DualA . choose . reverse . map getDualA-  chooseInt n = DualA $ (\x -> n - 1 - x) <$> chooseInt n--fstP :: Product p q a -> p a-fstP (Pair p _) = p--sndP :: Product p q a -> q a-sndP (Pair _ q) = q- -- | Derived instance. instance (Unfolder p, Unfolder q) => Unfolder (Product p q) where   choose ps = Pair (choose $ map fstP ps) (choose $ map sndP ps)+    where+      fstP (Pair p _) = p+      sndP (Pair _ q) = q   chooseInt n = Pair (chooseInt n) (chooseInt n)  -- | Derived instance.@@ -157,7 +157,7 @@  -- | Derived instance. instance (Functor m, Monad m) => Unfolder (MaybeT m) where-  choose ms = MaybeT $ fmap (listToMaybe . catMaybes) (mapM runMaybeT ms)+  choose ms = MaybeT $ listToMaybe . catMaybes <$> mapM runMaybeT ms   chooseInt 0 = MaybeT $ return Nothing   chooseInt _ = MaybeT $ return (Just 0)   @@ -194,6 +194,64 @@   chooseInt n = Random . StateT $ return . R.randomR (0, n - 1)  +-- | An 'UnfolderTransformer' changes the way an 'Unfolder' unfolds. +class UnfolderTransformer t where+  -- | Lift a computation from the argument unfolder to the constructed unfolder.+  lift :: Unfolder f => f a -> t f a++-- | Run an unfolding function with one argument using an 'UnfolderTransformer', given a way to run the transformer.+ala :: (UnfolderTransformer t, Unfolder f) => (t f b -> f b) -> (t f a -> t f b) -> f a -> f b+ala lower f = lower . f . lift++-- | Run an unfolding function with two arguments using an 'UnfolderTransformer', given a way to run the transformer.+ala2 :: (UnfolderTransformer t, Unfolder f) => (t f c -> f c) -> (t f a -> t f b -> t f c) -> f a -> f b -> f c+ala2 lower f = ala lower . f . lift+++-- | 'DualA' flips the @\<|>@ operator from `Alternative`.+newtype DualA f a = DualA { getDualA :: f a }+  deriving (Eq, Show, Functor, Applicative)++instance Alternative f => Alternative (DualA f) where+  empty = DualA empty+  DualA a <|> DualA b = DualA (b <|> a)++-- | Reverse the list passed to choose.+instance Unfolder f => Unfolder (DualA f) where+  choose = DualA . choose . reverse . map getDualA+  chooseInt n = DualA $ (\x -> n - 1 - x) <$> chooseInt n++instance UnfolderTransformer DualA where+  lift = DualA+++-- | Natural transformations+data NT f g = NT { getNT :: forall a. f a -> g a }++newtype WithRec f a = WithRec { getWithRec :: ReaderT (Int -> NT f f) f a }+  deriving (Functor, Applicative, Alternative)++-- | Applies a certain function depending on the depth at every recursive position.+instance Unfolder f => Unfolder (WithRec f) where+  choose ms = WithRec . ReaderT $ \f -> +    getNT (f 0) $ choose (map (\(WithRec (ReaderT m)) -> m (f . succ)) ms)++instance UnfolderTransformer WithRec where+  lift = WithRec . ReaderT . const++-- | Apply a certain function of type @f a -> f a@ to the result of a 'choose'.+-- The depth is passed as 'Int', so you can apply a different function at each depth.+-- Because of a @forall@, the function needs to be wrapped in a 'NT' constructor.+-- See 'limitDepth' for an example how to use this function.+withRec :: (Int -> NT f f) -> WithRec f a -> f a+withRec f = (`runReaderT` f) . getWithRec++-- | Limit the depth of an unfolding.+limitDepth :: Unfolder f => Int -> WithRec f a -> f a+limitDepth m = withRec (\d -> NT $ if d == m then const empty else id)+++ -- | Return a generator of values of a given depth. -- Returns 'Nothing' if there are no values of that depth or deeper. -- The depth is the number of 'choose' calls.@@ -216,9 +274,13 @@ instance Applicative f => Unfolder (BFS f) where   choose ms = BFS $ \d -> if d == 0 then Just [] else flattenBFS (map (`getBFS` (d - 1)) ms) -runBFS :: Unfolder f => BFS f x -> f x-runBFS (BFS f) = choose (loop 0) where loop d = maybe [] (++ loop (d + 1)) (f d)+instance UnfolderTransformer BFS where+  lift = packBFS +-- | Change the order of unfolding to be breadth-first.+bfs :: Unfolder f => BFS f x -> f x+bfs (BFS f) = choose (loop 0) where loop d = maybe [] (++ loop (d + 1)) (f d)+ packBFS :: f x -> BFS f x packBFS r = BFS $ \d -> if d == 0 then Just [r] else Nothing @@ -261,3 +323,18 @@  arbUnit :: Arbitrary a => Arb a arbUnit = Arb 0 (\r n -> Just $ unGen arbitrary r n)++-- | Variant of 'Data.Functor.Constant' that does multiplication of the constants for @\<*>@ and addition for @\<|>@.+newtype NumConst a x = NumConst { getNumConst :: a } deriving (Eq, Show)+instance Functor (NumConst a) where+  fmap _ (NumConst a) = NumConst a+instance Num a => Applicative (NumConst a) where+  pure _ = NumConst 1+  NumConst a <*> NumConst b = NumConst $ a * b+instance Num a => Alternative (NumConst a) where+  empty = NumConst 0+  NumConst a <|> NumConst b = NumConst $ a + b+-- | Unfolds to a constant numeric value. Useful for counting shapes.+instance Num a => Unfolder (NumConst a) where+  choose [] = empty+  choose as = foldr1 (<|>) as
unfoldable.cabal view
@@ -1,5 +1,5 @@ Name:                 unfoldable-Version:              0.5.0+Version:              0.6.0 Synopsis:             Class of data structures that can be unfolded. Description:          Just as there's a Foldable class, there should also be an Unfoldable class.                        .@@ -30,7 +30,8 @@      Exposed-modules:     Data.Unfolder-    Data.Unfoldable  +    Data.Unfoldable+    Data.Biunfoldable      Build-depends:       base         >= 4   && < 5