unfoldable-0.8: src/Data/Unfoldable.hs
-----------------------------------------------------------------------------
-- |
-- Module : Data.Unfoldable
-- 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 that can be unfolded.
-----------------------------------------------------------------------------
{-# LANGUAGE CPP, Safe #-}
#ifdef GENERICS
{-# LANGUAGE TypeOperators, DefaultSignatures, FlexibleContexts #-}
#endif
module Data.Unfoldable
(
-- * Unfoldable
Unfoldable(..)
, unfold_
, unfoldBF
, unfoldBF_
-- ** Specific unfolds
, unfoldr
, fromList
, leftMost
, rightMost
, allDepthFirst
, allToDepth
, allBreadthFirst
, randomDefault
, arbitraryDefault
)
where
import Control.Applicative
import Data.Unfolder
import Data.Functor.Compose
import Data.Functor.Constant
import Data.Functor.Identity
import Data.Functor.Product
import Data.Functor.Reverse
import Control.Monad.Trans.State
import qualified System.Random as R
import Test.QuickCheck (Arbitrary(..), Gen, sized, resize)
import Data.Maybe
#ifdef GENERICS
import GHC.Generics
#endif
-- | Data structures that can be unfolded.
--
-- For example, given a data type
--
-- > data Tree a = Empty | Leaf a | Node (Tree a) a (Tree a)
--
-- a suitable instance would be
--
-- > instance Unfoldable Tree where
-- > unfold fa = choose
-- > [ pure Empty
-- > , Leaf <$> fa
-- > , Node <$> unfold fa <*> fa <*> unfold fa
-- > ]
--
-- i.e. it follows closely the instance for 'Traversable', but instead of matching on an input value,
-- we 'choose' from a list of all cases.
--
-- Instead of manually writing the `Unfoldable` instance, you can add a @deriving@ `Generic1`
-- to your datatype and declare an `Unfoldable` instance without giving a definition for `unfold`.
--
-- For example the previous example can be simplified to just:
--
-- > {-# LANGUAGE DeriveGeneric #-}
-- >
-- > import GHC.Generics
-- >
-- > data Tree a = Empty | Leaf a | Node (Tree a) a (Tree a) deriving Generic1
-- >
-- > instance Unfoldable Tree
class Unfoldable t where
-- | Given a way to generate elements, return a way to generate structures containing those elements.
unfold :: Unfolder f => f a -> f (t a)
#ifdef GENERICS
default unfold :: (Generic1 t, GUnfold (Rep1 t), Unfolder f) => f a -> f (t a)
unfold fa = to1 <$> choose (gunfold fa)
class GUnfold r where
gunfold :: Unfolder f => f a -> [f (r a)]
instance GUnfold V1 where
gunfold _ = []
instance GUnfold U1 where
gunfold _ = [pure U1]
instance GUnfold Par1 where
gunfold fa = [Par1 <$> fa]
instance Unfoldable f => GUnfold (Rec1 f) where
gunfold fa = [Rec1 <$> unfold fa]
instance (Bounded c, Enum c) => GUnfold (K1 i c) where
gunfold _ = [K1 <$> boundedEnum]
instance GUnfold f => GUnfold (M1 i c f) where
gunfold fa = map (M1 <$>) (gunfold fa)
instance (GUnfold f, GUnfold g) => GUnfold (f :+: g) where
gunfold fa = map (L1 <$>) (gunfold fa) ++ map (R1 <$>) (gunfold fa)
instance (GUnfold f, GUnfold g) => GUnfold (f :*: g) where
gunfold fa = liftA2 (:*:) <$> gunfold fa <*> gunfold fa
instance (GUnfold f, GUnfold g) => GUnfold (f :.: g) where
gunfold fa = map (Comp1 <$>) (gunfold (choose (gunfold fa)))
#endif
-- | Unfold the structure, always using @()@ as elements.
unfold_ :: (Unfoldable t, Unfolder f) => f (t ())
unfold_ = unfold (pure ())
-- | Breadth-first unfold, which orders the result by the number of 'choose' calls.
unfoldBF :: (Unfoldable t, Unfolder f) => f a -> f (t a)
unfoldBF = ala bfs unfold
-- | Unfold the structure breadth-first, always using @()@ as elements.
unfoldBF_ :: (Unfoldable t, Unfolder f) => f (t ())
unfoldBF_ = bfs unfold_
-- | @unfoldr@ builds a data structure from a seed value. It can be specified as:
--
-- > unfoldr f z == fromList (Data.List.unfoldr f z)
unfoldr :: Unfoldable t => (b -> Maybe (a, b)) -> b -> Maybe (t a)
unfoldr f z = terminate . flip runStateT z . unfoldBF . StateT $ maybeToList . f
where
terminate [] = Nothing
terminate ((t, b):ts) = if isNothing (f b) then Just t else terminate ts
-- | Create a data structure using the list 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 list.
fromList :: Unfoldable t => [a] -> Maybe (t a)
fromList = unfoldr uncons
where
uncons [] = Nothing
uncons (a:as) = Just (a, as)
-- | Always choose the first constructor.
leftMost :: Unfoldable t => Maybe (t ())
leftMost = unfold_
-- | Always choose the last constructor.
rightMost :: Unfoldable t => Maybe (t ())
rightMost = getDualA unfold_
-- | Generate all the values depth-first.
allDepthFirst :: Unfoldable t => [t ()]
allDepthFirst = unfold_
-- | 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_
-- | Generate a random value, can be used as default instance for 'R.Random'.
randomDefault :: (R.Random a, R.RandomGen g, Unfoldable t) => g -> (t a, g)
randomDefault = runState . getRandom . unfold . Random . state $ R.random
-- | Provides a QuickCheck generator, can be used as default instance for 'Arbitrary'.
arbitraryDefault :: (Arbitrary a, Unfoldable t) => Gen (t a)
arbitraryDefault = let Arb _ gen = unfold arbUnit in
fromMaybe (error "Failed to generate a value.") <$> sized (\n -> resize (n + 1) gen)
instance Unfoldable [] where
unfold fa = choose
[ pure []
, (:) <$> fa <*> unfold fa
]
instance Unfoldable Maybe where
unfold fa = choose
[ pure Nothing
, Just <$> fa
]
instance (Bounded a, Enum a) => Unfoldable (Either a) where
unfold fa = choose
[ Left <$> boundedEnum
, Right <$> fa
]
instance (Bounded a, Enum a) => Unfoldable ((,) a) where
unfold fa = choose
[ (,) <$> boundedEnum <*> fa ]
instance Unfoldable Identity where
unfold fa = choose
[ Identity <$> fa ]
instance (Bounded a, Enum a) => Unfoldable (Constant a) where
unfold _ = choose
[ Constant <$> boundedEnum ]
instance (Unfoldable p, Unfoldable q) => Unfoldable (Product p q) where
unfold fa = choose
[ Pair <$> unfold fa <*> unfold fa ]
instance (Unfoldable p, Unfoldable q) => Unfoldable (Compose p q) where
unfold fa = choose
[ Compose <$> unfold (unfold fa) ]
instance Unfoldable f => Unfoldable (Reverse f) where
unfold fa = choose
[ Reverse <$> getDualA (unfold (DualA fa)) ]