-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathUnfolder.hs
426 lines (338 loc) · 14.4 KB
/
Unfolder.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
-----------------------------------------------------------------------------
-- |
-- Module : Data.Unfolder
-- Copyright : (c) Sjoerd Visscher 2014
-- License : BSD-style (see the file LICENSE)
--
-- Maintainer : sjoerd@w3future.com
-- Stability : experimental
-- Portability : non-portable
--
-- Unfolders provide a way to unfold data structures.
-- They are basically 'Alternative' instances, but the 'choose' method
-- allows the unfolder to do something special for the recursive positions
-- of the data structure.
-----------------------------------------------------------------------------
{-# LANGUAGE
GeneralizedNewtypeDeriving
, RankNTypes
, Trustworthy
, CPP
#-}
#if !defined(MIN_VERSION_containers)
#define MIN_VERSION_containers(x,y,z) 0
#endif
module Data.Unfolder
(
-- * Unfolder
Unfolder(..)
, chooseMonadDefault
, chooseMapMonadDefault
, between
, betweenD
, boundedEnum
, boundedEnumD
-- ** Unfolder instances
, Random(..)
, Arb(..)
, arbUnit
, NumConst(..)
, Nth(..)
-- * UnfolderTransformer
, UnfolderTransformer(..)
, ala
, ala2
, ala3
-- ** UnfolderTransformer instances
, DualA(..)
, NT(..)
, WithRec(..)
, withRec
, limitDepth
, BFS(..)
, Split
, bfs
, bfsBySum
)
where
import Control.Applicative
import Control.Monad
import Control.Arrow (ArrowZero, ArrowPlus)
import Data.Functor.Product
import Data.Functor.Compose
import Data.Functor.Reverse
import Control.Applicative.Backwards
import Control.Applicative.Lift
import Control.Monad.Trans.Except
import Control.Monad.Trans.List
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.RWS
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Control.Monad.Trans.Writer
import qualified System.Random as R
import Test.QuickCheck (Arbitrary(..), Gen, oneof, elements, frequency, sized, resize)
import Data.Monoid (Monoid(..))
import Data.Maybe (catMaybes)
import qualified Data.Sequence as S
-- | Unfolders provide a way to unfold data structures.
-- The methods have default implementations in terms of 'Alternative',
-- but you can implement 'chooseMap' to act on recursive positions of the
-- data structure, or simply to provide a faster implementation than
-- 'foldr ((<|>) . f) empty'.
class Alternative f => Unfolder f where
-- | Choose one of the values from the list.
choose :: [f a] -> f a
choose = chooseMap id
-- | Choose one of the values from the list and apply the given function.
chooseMap :: (a -> f b) -> [a] -> f b
chooseMap f = foldr ((<|>) . f) empty
-- | Given a number 'n', return a number between '0' and 'n - 1'.
chooseInt :: Int -> f Int
chooseInt n = chooseMap pure [0 .. n - 1]
-- | If an unfolder is monadic, 'choose' can be implemented in terms of 'chooseInt'.
chooseMonadDefault :: (Monad m, Unfolder m) => [m a] -> m a
chooseMonadDefault ms = chooseInt (length ms) >>= (ms !!)
-- | If an unfolder is monadic, 'chooseMap' can be implemented in terms of 'chooseInt'.
chooseMapMonadDefault :: (Monad m, Unfolder m) => (a -> m b) -> [a] -> m b
chooseMapMonadDefault f as = chooseInt (length as) >>= f . (as !!)
-- | If a datatype is 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 @*@).
between :: (Unfolder f, Enum a) => a -> a -> f a
between lb ub = (\x -> toEnum (x + fromEnum lb)) <$> chooseInt (1 + fromEnum ub - fromEnum lb)
-- | If a datatype is also bounded, we can choose between all possible values.
--
-- > boundedEnum = between minBound maxBound
boundedEnum :: (Unfolder f, Bounded a, Enum a) => f a
boundedEnum = between minBound maxBound
-- | 'betweenD' uses 'choose' to generate a value. It chooses between the lower bound and one
-- of the higher values. This means that f.e. breadth-first unfolding and arbitrary will prefer
-- lower values.
betweenD :: (Unfolder f, Enum a) => a -> a -> f a
betweenD lb0 ub = betweenD' lb0 (fromEnum ub - fromEnum lb0)
where
betweenD' lb n | n < 0 = empty
| otherwise = choose [pure lb, betweenD' (succ lb) (pred n)]
-- | > boundedEnumD = betweenD minBound maxBound
boundedEnumD :: (Unfolder f, Bounded a, Enum a) => f a
boundedEnumD = betweenD minBound maxBound
-- | Derived instance.
instance MonadPlus m => Unfolder (WrappedMonad m)
-- | Derived instance.
instance (ArrowZero a, ArrowPlus a) => Unfolder (WrappedArrow a b)
-- | Don't choose but return all items.
instance Unfolder [] where
choose = concat
chooseMap = concatMap
chooseInt n = [0 .. n - 1]
-- | Always choose the first item.
instance Unfolder Maybe where
choose = foldr const Nothing
chooseMap f = foldr (const . f) Nothing
chooseInt 0 = Nothing
chooseInt _ = Just 0
-- | Derived instance.
instance (Unfolder p, Unfolder q) => Unfolder (Product p q) where
chooseMap f as = Pair (chooseMap (fstP . f) as) (chooseMap (sndP . f) as)
where
fstP (Pair p _) = p
sndP (Pair _ q) = q
chooseInt n = Pair (chooseInt n) (chooseInt n)
-- | Derived instance.
instance (Unfolder p, Applicative q) => Unfolder (Compose p q) where
chooseMap f = Compose . chooseMap (getCompose . f)
chooseInt n = Compose $ pure <$> chooseInt n
-- | Derived instance.
instance Unfolder f => Unfolder (Reverse f) where
chooseMap f = Reverse . chooseMap (getReverse . f)
chooseInt n = Reverse $ chooseInt n
-- | Derived instance.
instance Unfolder f => Unfolder (Backwards f) where
chooseMap f = Backwards . chooseMap (forwards . f)
chooseInt n = Backwards $ chooseInt n
-- | Derived instance.
instance Unfolder f => Unfolder (Lift f)
-- | Derived instance.
instance (Functor m, Monad m, Monoid e) => Unfolder (ExceptT e m)
-- | Derived instance.
instance Applicative f => Unfolder (ListT f) where
{-# INLINABLE chooseMap #-}
chooseMap f = ListT . foldr appRun (pure [])
where
appRun x ys = (++) <$> runListT (f x) <*> ys
chooseInt n = ListT $ pure [0 .. n - 1]
-- | Derived instance.
instance (Functor m, Monad m) => Unfolder (MaybeT m) where
chooseMap _ [] = MaybeT (return Nothing)
chooseMap f (a : as) = MaybeT $ do
res <- runMaybeT (f a)
case res of
Nothing -> runMaybeT $ chooseMap f as
Just _ -> return res
chooseInt 0 = MaybeT $ return Nothing
chooseInt _ = MaybeT $ return (Just 0)
-- | Derived instance.
instance (Monoid w, MonadPlus m, Unfolder m) => Unfolder (RWST r w s m) where
chooseMap f as = RWST $ \r s -> chooseMap (\a -> runRWST (f a) r s) as
-- | Derived instance.
instance (MonadPlus m, Unfolder m) => Unfolder (StateT s m) where
chooseMap f as = StateT $ \s -> chooseMap (\a -> f a `runStateT` s) as
-- | Derived instance.
instance Unfolder m => Unfolder (ReaderT r m) where
chooseMap f as = ReaderT $ \r -> chooseMap (\a -> f a `runReaderT` r) as
-- | Derived instance.
instance (Monoid w, Unfolder m) => Unfolder (WriterT w m) where
chooseMap f = WriterT . chooseMap (runWriterT . f)
-- | Don't choose but return all items.
instance Unfolder S.Seq where
#if MIN_VERSION_containers(0,5,6)
chooseInt n = S.fromFunction n id
#endif
newtype Random g m a = Random { getRandom :: StateT g m a }
deriving (Functor, Applicative, Monad)
instance (Functor m, Monad m, R.RandomGen g) => Alternative (Random g m) where
empty = choose []
a <|> b = choose [a, b]
instance (Functor m, Monad m, R.RandomGen g) => MonadPlus (Random g m) where
mzero = choose []
mplus a b = choose [a, b]
-- | Choose randomly.
instance (Functor m, Monad m, R.RandomGen g) => Unfolder (Random g m) where
choose = chooseMonadDefault
chooseMap = chooseMapMonadDefault
chooseInt n = Random . StateT $ return . R.randomR (0, n - 1)
-- | A variant of Test.QuickCheck.Gen, with failure
-- and a count of the number of recursive positions and parameter positions.
data Arb a = Arb Int Int (Gen (Maybe a))
instance Functor Arb where
fmap f (Arb r p g) = Arb r p $ fmap (fmap f) g
instance Applicative Arb where
pure = Arb 0 0 . pure . pure
Arb r1 p1 ff <*> Arb r2 p2 fx = Arb (r1 + r2) (p1 + p2) $ liftA2 (<*>) ff fx
instance Alternative Arb where
empty = Arb 0 0 (pure Nothing)
Arb r1 p1 g1 <|> Arb r2 p2 g2 = Arb (r1 + r2) (p1 + p2) $ g1 >>= \a -> g2 >>= \b -> Just <$> elements (catMaybes [a, b])
-- | Limit the depth of the generated data structure by
-- dividing the given size by the number of recursive positions.
instance Unfolder Arb where
choose as = Arb 1 0 $ sized g
where
g n = freq $ foldMap f as
where
(recPosCount, parPosCount) = foldr (\(Arb r p _) (rc, pc) -> (r + rc, p + pc)) (0, 0) as
recSize = (n - parPosCount) `div` max 1 recPosCount
f (Arb r p gen) = if (r > 0 && recSize < 0) || (n == 0 && r + p > 0) then [] else [(3 + r * recSize, resize (max 0 recSize) gen)]
freq [] = pure Nothing
freq as = frequency as
arbUnit :: Arbitrary a => Arb a
arbUnit = Arb 0 1 (Just <$> arbitrary)
-- | 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)
data Nth a = Nth
{ size :: Integer
, getNth :: Integer -> a
}
instance Functor Nth where
fmap f (Nth sizeA as) = Nth sizeA (f . as)
instance Applicative Nth where
pure a = Nth 1 (const a)
Nth sizeF fs <*> Nth sizeA as = Nth (sizeF * sizeA) $ \n ->
let (l, r) = n `divMod` sizeA in fs l (as r)
instance Alternative Nth where
empty = Nth 0 (const undefined)
Nth sizeA as <|> Nth sizeB bs = Nth (sizeA + sizeB) $ \n ->
if n < sizeA then as n else bs (n - sizeA)
-- | Get the nth value from the sequence of all possible values.
instance Unfolder Nth where
chooseInt n = Nth (toInteger n) fromInteger
-- | 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
-- | Run an unfolding function with three arguments using an 'UnfolderTransformer', given a way to run the transformer.
ala3 :: (UnfolderTransformer t, Unfolder f) => (t f d -> f d) -> (t f a -> t f b -> t f c -> t f d) -> f a -> f b -> f c -> f d
ala3 lower f = ala2 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
chooseMap f = DualA . chooseMap (getDualA . f) . reverse
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
chooseMap h as = WithRec . ReaderT $ \f ->
getNT (f 0) $ chooseMap (withRec (f . succ) . h) as
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.
newtype BFS f x = BFS { getBFS :: (Int, Split) -> Maybe [f x] }
type Split = Int -> [(Int, Int)]
instance Functor f => Functor (BFS f) where
fmap f = BFS . (fmap (map (fmap f)) .) . getBFS
instance Applicative f => Applicative (BFS f) where
pure = packBFS . pure
BFS ff <*> BFS fx = BFS $ \(d, split) -> flattenBFS $
[ liftA2 (liftA2 (<*>)) (ff (i, split)) (fx (j, split)) | (i, j) <- split d ]
instance Applicative f => Alternative (BFS f) where
empty = BFS $ \(d, _) -> if d == 0 then Just [] else Nothing
BFS fa <|> BFS fb = BFS $ \d -> flattenBFS [fa d, fb d]
-- | Choose between values of a given depth only.
instance Applicative f => Unfolder (BFS f) where
chooseMap f as = BFS $ \(d, split) -> if d == 0 then Just [] else flattenBFS (map (\a -> f a `getBFS` (d - 1, split)) as)
instance UnfolderTransformer BFS where
lift = packBFS
bySum :: Split
bySum d = [(i, d - i)| i <- [0 .. d]]
byMax :: Split
byMax d = [(i, d)| i <- [0 .. d - 1]] ++ [(d, i)| i <- [0 .. d]]
bfsBy :: Unfolder f => Split -> BFS f x -> f x
bfsBy split (BFS f) = choose (loop 0) where loop d = maybe [] (++ loop (d + 1)) (f (d, split))
-- | Change the order of unfolding to be breadth-first, by maximum depth of the components.
bfs :: Unfolder f => BFS f x -> f x
bfs = bfsBy byMax
-- | Change the order of unfolding to be breadth-first, by the sum of depths of the components.
bfsBySum :: Unfolder f => BFS f x -> f x
bfsBySum = bfsBy bySum
packBFS :: f x -> BFS f x
packBFS r = BFS $ \(d, _) -> if d == 0 then Just [r] else Nothing
flattenBFS :: [Maybe [a]] -> Maybe [a]
flattenBFS ms = case catMaybes ms of
[] -> Nothing
ms' -> Just (concat ms')