Skip to content

Commit 3a177c7

Browse files
committed
Clean up <*> some more
1. Remove all partial functions and all "impossible" errors. 2. Simplify the way the sequence pieces are put together at the bottom. The immediate-indexing `<*>` test improves from 1.44 microseconds to 1.24 microseconds. The other `<*>` tests improve very slightly.
1 parent dade165 commit 3a177c7

File tree

1 file changed

+141
-128
lines changed

1 file changed

+141
-128
lines changed

Data/Sequence.hs

Lines changed: 141 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -271,53 +271,67 @@ instance Monad Seq where
271271

272272
instance Applicative Seq where
273273
pure = singleton
274-
275-
Seq Empty <*> xs = xs `seq` empty
276-
fs <*> Seq Empty = fs `seq` empty
277-
fs <*> Seq (Single (Elem x)) = fmap ($ x) fs
278-
fs <*> xs
279-
| length fs < 4 = foldl' add empty fs
280-
where add ys f = ys >< fmap f xs
281-
fs <*> xs | length xs < 4 = apShort fs xs
282-
fs <*> xs = apty fs xs
283-
284274
xs *> ys = replicateSeq (length xs) ys
285275

286-
-- <*> when the length of the first argument is at least two and
287-
-- the length of the second is two or three.
288-
apShort :: Seq (a -> b) -> Seq a -> Seq b
289-
apShort (Seq fs) xs = Seq $ case toList xs of
290-
[a,b] -> ap2FT fs (a,b)
291-
[a,b,c] -> ap3FT fs (a,b,c)
292-
_ -> error "apShort: not 2-3"
293-
294-
ap2FT :: FingerTree (Elem (a->b)) -> (a,a) -> FingerTree (Elem b)
295-
ap2FT fs (x,y) = Deep (size fs * 2)
276+
fs <*> xs = case viewl fs of
277+
EmptyL -> empty
278+
firstf :< fs' -> case viewr fs' of
279+
EmptyR -> fmap firstf xs
280+
Seq fs''FT :> lastf -> case (rigidify . (\(Seq a) -> a)) xs of
281+
RigidEmpty -> empty
282+
RigidOne (Elem x) -> fmap ($x) fs
283+
RigidTwo (Elem x1) (Elem x2) ->
284+
Seq $ ap2FT firstf fs''FT lastf (x1, x2)
285+
RigidThree (Elem x1) (Elem x2) (Elem x3) ->
286+
Seq $ ap3FT firstf fs''FT lastf (x1, x2, x3)
287+
RigidFull r@(Rigid s pr _m sf) -> Seq $
288+
Deep (s * length fs)
289+
(fmap (fmap firstf) (nodeToDigit pr))
290+
(aptyMiddle (fmap firstf) (fmap lastf) fmap fs''FT r)
291+
(fmap (fmap lastf) (nodeToDigit sf))
292+
293+
294+
ap2FT :: (a -> b) -> FingerTree (Elem (a->b)) -> (a -> b) -> (a,a) -> FingerTree (Elem b)
295+
ap2FT firstf fs lastf (x,y) =
296+
Deep (size fs * 2 + 4)
296297
(Two (Elem $ firstf x) (Elem $ firstf y))
297-
(mapMulFT 2 (\(Elem f) -> Node2 2 (Elem (f x)) (Elem (f y))) m)
298+
(mapMulFT 2 (\(Elem f) -> Node2 2 (Elem (f x)) (Elem (f y))) fs)
298299
(Two (Elem $ lastf x) (Elem $ lastf y))
299-
where
300-
(Elem firstf, m, Elem lastf) = trimTree fs
301300

302-
ap3FT :: FingerTree (Elem (a->b)) -> (a,a,a) -> FingerTree (Elem b)
303-
ap3FT fs (x,y,z) = Deep (size fs * 3)
301+
ap3FT :: (a -> b) -> FingerTree (Elem (a->b)) -> (a -> b) -> (a,a,a) -> FingerTree (Elem b)
302+
ap3FT firstf fs lastf (x,y,z) = Deep (size fs * 3 + 6)
304303
(Three (Elem $ firstf x) (Elem $ firstf y) (Elem $ firstf z))
305-
(mapMulFT 3 (\(Elem f) -> Node3 3 (Elem (f x)) (Elem (f y)) (Elem (f z))) m)
304+
(mapMulFT 3 (\(Elem f) -> Node3 3 (Elem (f x)) (Elem (f y)) (Elem (f z))) fs)
306305
(Three (Elem $ lastf x) (Elem $ lastf y) (Elem $ lastf z))
307-
where
308-
(Elem firstf, m, Elem lastf) = trimTree fs
309-
310-
-- <*> when the length of each argument is at least four.
311-
apty :: Seq (a -> b) -> Seq a -> Seq b
312-
apty (Seq fs) (Seq xs@Deep{}) = Seq $
313-
Deep (s' * size fs)
314-
(fmap (fmap firstf) pr')
315-
(aptyMiddle (fmap firstf) (fmap lastf) fmap fs' xs')
316-
(fmap (fmap lastf) sf')
317-
where
318-
(Elem firstf, fs', Elem lastf) = trimTree fs
319-
xs'@(Deep s' pr' _m' sf') = rigidify xs
320-
apty _ _ = error "apty: expects a Deep constructor"
306+
307+
308+
data Rigidified a = RigidEmpty
309+
| RigidOne a
310+
| RigidTwo a a
311+
| RigidThree a a a
312+
| RigidFull (Rigid a)
313+
#ifdef TESTING
314+
deriving Show
315+
#endif
316+
317+
data Rigid a = Rigid {-# UNPACK #-} !Int !(Digit23 a) (Thin (Node a)) !(Digit23 a)
318+
#ifdef TESTING
319+
deriving Show
320+
#endif
321+
322+
data Thin a = EmptyTh
323+
| SingleTh a
324+
| DeepTh {-# UNPACK #-} !Int !(Digit12 a) (Thin (Node a)) !(Digit12 a)
325+
#ifdef TESTING
326+
deriving Show
327+
#endif
328+
329+
data Digit12 a = One12 a | Two12 a a
330+
#ifdef TESTING
331+
deriving Show
332+
#endif
333+
334+
type Digit23 a = Node a
321335

322336
-- | 'aptyMiddle' does most of the hard work of computing @fs<*>xs@.
323337
-- It produces the center part of a finger tree, with a prefix corresponding
@@ -333,55 +347,56 @@ aptyMiddle
333347
-> (c -> d)
334348
-> ((a -> b) -> c -> d)
335349
-> FingerTree (Elem (a -> b))
336-
-> FingerTree c
350+
-> Rigid c
337351
-> FingerTree (Node d)
352+
338353
-- Not at the bottom yet
354+
339355
aptyMiddle firstf
340356
lastf
341357
map23
342358
fs
343-
(Deep s pr (Deep sm prm mm sfm) sf)
359+
(Rigid s pr (DeepTh sm prm mm sfm) sf)
344360
= Deep (sm + s * (size fs + 1)) -- note: sm = s - size pr - size sf
345-
(fmap (fmap firstf) prm)
361+
(fmap (fmap firstf) (digit12ToDigit prm))
346362
(aptyMiddle (fmap firstf)
347363
(fmap lastf)
348-
(\f -> fmap (map23 f))
364+
(fmap . map23)
349365
fs
350-
(Deep s (squashL pr prm) mm (squashR sfm sf)))
351-
(fmap (fmap lastf) sfm)
366+
(Rigid s (squashL pr prm) mm (squashR sfm sf)))
367+
(fmap (fmap lastf) (digit12ToDigit sfm))
368+
369+
-- At the bottom
370+
371+
aptyMiddle firstf
372+
lastf
373+
map23
374+
fs
375+
(Rigid s pr EmptyTh sf)
376+
= deep
377+
(One (fmap firstf sf))
378+
(mapMulFT s (\(Elem f) -> fmap (fmap (map23 f)) converted) fs)
379+
(One (fmap lastf pr))
380+
where converted = node2 pr sf
352381

353-
-- At the bottom. Note that these appendTree0 calls are very cheap, because in
354-
-- each case, one of the arguments is guaranteed to be Empty or Single.
355382
aptyMiddle firstf
356383
lastf
357384
map23
358385
fs
359-
(Deep s pr m sf)
360-
= fmap (fmap firstf) m `appendTree0`
361-
((fmap firstf (digitToNode sf)
362-
`consTree` middle)
363-
`snocTree` fmap lastf (digitToNode pr))
364-
`appendTree0` fmap (fmap lastf) m
365-
where middle = case trimTree $ mapMulFT s (\(Elem f) -> fmap (fmap (map23 f)) converted) fs of
366-
(firstMapped, restMapped, lastMapped) ->
367-
Deep (size firstMapped + size restMapped + size lastMapped)
368-
(nodeToDigit firstMapped) restMapped (nodeToDigit lastMapped)
369-
converted = case m of
370-
Empty -> Node2 s lconv rconv
371-
Single q -> Node3 s lconv q rconv
372-
Deep{} -> error "aptyMiddle: impossible"
373-
lconv = digitToNode pr
374-
rconv = digitToNode sf
375-
376-
aptyMiddle _ _ _ _ _ = error "aptyMiddle: expected Deep finger tree"
386+
(Rigid s pr (SingleTh q) sf)
387+
= deep
388+
(Two (fmap firstf q) (fmap firstf sf))
389+
(mapMulFT s (\(Elem f) -> fmap (fmap (map23 f)) converted) fs)
390+
(Two (fmap lastf pr) (fmap lastf q))
391+
where converted = node3 pr q sf
377392

378393
{-# SPECIALIZE
379394
aptyMiddle
380395
:: (Node c -> d)
381396
-> (Node c -> d)
382397
-> ((a -> b) -> Node c -> d)
383398
-> FingerTree (Elem (a -> b))
384-
-> FingerTree (Node c)
399+
-> Rigid (Node c)
385400
-> FingerTree (Node d)
386401
#-}
387402
{-# SPECIALIZE
@@ -390,33 +405,24 @@ aptyMiddle _ _ _ _ _ = error "aptyMiddle: expected Deep finger tree"
390405
-> (Elem c -> d)
391406
-> ((a -> b) -> Elem c -> d)
392407
-> FingerTree (Elem (a -> b))
393-
-> FingerTree (Elem c)
408+
-> Rigid (Elem c)
394409
-> FingerTree (Node d)
395410
#-}
396411

397-
digitToNode :: Sized a => Digit a -> Node a
398-
digitToNode (Two a b) = node2 a b
399-
digitToNode (Three a b c) = node3 a b c
400-
digitToNode _ = error "digitToNode: not representable as a node"
401-
402-
type Digit23 = Digit
403-
type Digit12 = Digit
412+
digit12ToDigit :: Digit12 a -> Digit a
413+
digit12ToDigit (One12 a) = One a
414+
digit12ToDigit (Two12 a b) = Two a b
404415

405416
-- Squash the first argument down onto the left side of the second.
406417
squashL :: Sized a => Digit23 a -> Digit12 (Node a) -> Digit23 (Node a)
407-
squashL (Two a b) (One n) = Two (node2 a b) n
408-
squashL (Two a b) (Two n1 n2) = Three (node2 a b) n1 n2
409-
squashL (Three a b c) (One n) = Two (node3 a b c) n
410-
squashL (Three a b c) (Two n1 n2) = Three (node3 a b c) n1 n2
411-
squashL _ _ = error "squashL: wrong digit types"
418+
squashL m (One12 n) = node2 m n
419+
squashL m (Two12 n1 n2) = node3 m n1 n2
412420

413421
-- Squash the second argument down onto the right side of the first
414422
squashR :: Sized a => Digit12 (Node a) -> Digit23 a -> Digit23 (Node a)
415-
squashR (One n) (Two a b) = Two n (node2 a b)
416-
squashR (Two n1 n2) (Two a b) = Three n1 n2 (node2 a b)
417-
squashR (One n) (Three a b c) = Two n (node3 a b c)
418-
squashR (Two n1 n2) (Three a b c) = Three n1 n2 (node3 a b c)
419-
squashR _ _ = error "squashR: wrong digit types"
423+
squashR (One12 n) m = node2 n m
424+
squashR (Two12 n1 n2) m = node3 n1 n2 m
425+
420426

421427
-- | /O(m*n)/ (incremental) Takes an /O(m)/ function and a finger tree of size
422428
-- /n/ and maps the function over the tree leaves. Unlike the usual 'fmap', the
@@ -435,72 +441,81 @@ mapMulNode mul f (Node2 s a b) = Node2 (mul * s) (f a) (f b)
435441
mapMulNode mul f (Node3 s a b c) = Node3 (mul * s) (f a) (f b) (f c)
436442

437443

438-
trimTree :: Sized a => FingerTree a -> (a, FingerTree a, a)
439-
trimTree Empty = error "trim: empty tree"
440-
trimTree Single{} = error "trim: singleton"
441-
trimTree t = case splitTree 0 t of
442-
Split _ hd r ->
443-
case splitTree (size r - 1) r of
444-
Split m tl _ -> (hd, m, tl)
444+
-- rigidify :: Seq a -> Rigidified (Elem a)
445+
-- rigidify (Seq xs) = rigidify' xs
445446

446447
-- | /O(log n)/ (incremental) Takes the extra flexibility out of a 'FingerTree'
447448
-- to make it a genuine 2-3 finger tree. The result of 'rigidify' will have
448449
-- only 'Two' and 'Three' digits at the top level and only 'One' and 'Two'
449450
-- digits elsewhere. It gives an error if the tree has fewer than four
450451
-- elements.
451-
rigidify :: Sized a => FingerTree a -> FingerTree a
452+
rigidify :: FingerTree (Elem a) -> Rigidified (Elem a)
452453
-- Note that 'rigidify' may call itself, but it will do so at most
453454
-- once: each call to 'rigidify' will either fix the whole tree or fix one digit
454455
-- and leave the other alone. The patterns below just fix up the top level of
455456
-- the tree; 'rigidify' delegates the hard work to 'thin'.
456457

457458
-- The top of the tree is fine.
458-
rigidify (Deep s pr@Two{} m sf@Three{}) = Deep s pr (thin m) sf
459-
rigidify (Deep s pr@Three{} m sf@Three{}) = Deep s pr (thin m) sf
460-
rigidify (Deep s pr@Two{} m sf@Two{}) = Deep s pr (thin m) sf
461-
rigidify (Deep s pr@Three{} m sf@Two{}) = Deep s pr (thin m) sf
459+
460+
rigidify (Deep s (Two a b) m (Three c d e)) =
461+
RigidFull $ Rigid s (node2 a b) (thin m) (node3 c d e)
462+
rigidify (Deep s (Three a b c) m (Three d e f)) =
463+
RigidFull $ Rigid s (node3 a b c) (thin m) (node3 d e f)
464+
rigidify (Deep s (Two a b) m (Two c d)) =
465+
RigidFull $ Rigid s (node2 a b) (thin m) (node2 c d)
466+
rigidify (Deep s (Three a b c) m (Two d e)) =
467+
RigidFull $ Rigid s (node3 a b c) (thin m) (node2 d e)
462468

463469
-- One of the Digits is a Four.
464470
rigidify (Deep s (Four a b c d) m sf) =
465471
rigidify $ Deep s (Two a b) (node2 c d `consTree` m) sf
472+
466473
rigidify (Deep s pr m (Four a b c d)) =
467474
rigidify $ Deep s pr (m `snocTree` node2 a b) (Two c d)
468475

469-
-- One of the Digits is a One. If the middle is empty, we can only rigidify the
470-
-- tree if the other Digit is a Three.
471-
rigidify (Deep s (One a) Empty (Three b c d)) = Deep s (Two a b) Empty (Two c d)
472-
rigidify (Deep s (One a) m sf) = rigidify $ case viewLTree m of
473-
Just2 (Node2 _ b c) m' -> Deep s (Three a b c) m' sf
474-
Just2 (Node3 _ b c d) m' -> Deep s (Two a b) (node2 c d `consTree` m') sf
475-
Nothing2 -> error "rigidify: small tree"
476-
rigidify (Deep s (Three a b c) Empty (One d)) = Deep s (Two a b) Empty (Two c d)
477-
rigidify (Deep s pr m (One e)) = rigidify $ case viewRTree m of
478-
Just2 m' (Node2 _ a b) -> Deep s pr m' (Three a b e)
479-
Just2 m' (Node3 _ a b c) -> Deep s pr (m' `snocTree` node2 a b) (Two c e)
480-
Nothing2 -> error "rigidify: small tree"
481-
rigidify Empty = error "rigidify: empty tree"
482-
rigidify Single{} = error "rigidify: singleton"
476+
-- One of the Digits is a One.
477+
rigidify (Deep s (One a) m sf) = case viewLTree m of
478+
Just2 (Node2 _ b c) m' -> rigidify $ Deep s (Three a b c) m' sf
479+
Just2 (Node3 _ b c d) m' -> rigidify $ Deep s (Two a b) (node2 c d `consTree` m') sf
480+
Nothing2 -> case sf of
481+
One b -> RigidTwo a b
482+
Two b c -> RigidThree a b c
483+
Three b c d -> RigidFull $ Rigid s (node2 a b) EmptyTh (node2 c d)
484+
Four b c d e -> RigidFull $ Rigid s (node3 a b c) EmptyTh (node2 d e)
485+
486+
rigidify (Deep s pr m (One e)) = case viewRTree m of
487+
Just2 m' (Node2 _ a b) -> rigidify $ Deep s pr m' (Three a b e)
488+
Just2 m' (Node3 _ a b c) -> rigidify $ Deep s pr (m' `snocTree` node2 a b) (Two c e)
489+
Nothing2 -> case pr of
490+
One a -> RigidTwo a e
491+
Two a b -> RigidThree a b e
492+
Three a b c -> RigidFull $ Rigid s (node2 a b) EmptyTh (node2 c e)
493+
Four a b c d -> RigidFull $ Rigid s (node3 a b c) EmptyTh (node2 d e)
494+
495+
rigidify Empty = RigidEmpty
496+
497+
rigidify (Single q) = RigidOne q
498+
483499

484500
-- | /O(log n)/ (incremental) Rejigger a finger tree so the digits are all ones
485501
-- and twos.
486-
thin :: Sized a => FingerTree a -> FingerTree a
502+
thin :: Sized a => FingerTree a -> Thin a
487503
-- Note that 'thin12' will produce a 'Deep' constructor immediately before
488504
-- recursively calling 'thin'.
489-
thin Empty = Empty
490-
thin (Single a) = Single a
491-
thin t@(Deep s pr m sf) =
505+
thin Empty = EmptyTh
506+
thin (Single a) = SingleTh a
507+
thin (Deep s pr m sf) =
492508
case pr of
493-
One{} -> thin12 t
494-
Two{} -> thin12 t
495-
Three a b c -> thin12 $ Deep s (One a) (node2 b c `consTree` m) sf
496-
Four a b c d -> thin12 $ Deep s (Two a b) (node2 c d `consTree` m) sf
509+
One a -> thin12 s (One12 a) m sf
510+
Two a b -> thin12 s (Two12 a b) m sf
511+
Three a b c -> thin12 s (One12 a) (node2 b c `consTree` m) sf
512+
Four a b c d -> thin12 s (Two12 a b) (node2 c d `consTree` m) sf
497513

498-
thin12 :: Sized a => FingerTree a -> FingerTree a
499-
thin12 (Deep s pr m sf@One{}) = Deep s pr (thin m) sf
500-
thin12 (Deep s pr m sf@Two{}) = Deep s pr (thin m) sf
501-
thin12 (Deep s pr m (Three a b c)) = Deep s pr (thin $ m `snocTree` node2 a b) (One c)
502-
thin12 (Deep s pr m (Four a b c d)) = Deep s pr (thin $ m `snocTree` node2 a b) (Two c d)
503-
thin12 _ = error "thin12 expects a Deep FingerTree."
514+
thin12 :: Sized a => Int -> Digit12 a -> FingerTree (Node a) -> Digit a -> Thin a
515+
thin12 s pr m (One a) = DeepTh s pr (thin m) (One12 a)
516+
thin12 s pr m (Two a b) = DeepTh s pr (thin m) (Two12 a b)
517+
thin12 s pr m (Three a b c) = DeepTh s pr (thin $ m `snocTree` node2 a b) (One12 c)
518+
thin12 s pr m (Four a b c d) = DeepTh s pr (thin $ m `snocTree` node2 a b) (Two12 c d)
504519

505520

506521
instance MonadPlus Seq where
@@ -975,9 +990,7 @@ Seq xs >< Seq ys = Seq (appendTree0 xs ys)
975990

976991
-- The appendTree/addDigits gunk below is machine generated
977992

978-
{-# SPECIALIZE appendTree0 :: FingerTree (Elem a) -> FingerTree (Elem a) -> FingerTree (Elem a) #-}
979-
{-# SPECIALIZE appendTree0 :: FingerTree (Node a) -> FingerTree (Node a) -> FingerTree (Node a) #-}
980-
appendTree0 :: Sized a => FingerTree a -> FingerTree a -> FingerTree a
993+
appendTree0 :: FingerTree (Elem a) -> FingerTree (Elem a) -> FingerTree (Elem a)
981994
appendTree0 Empty xs =
982995
xs
983996
appendTree0 xs Empty =

0 commit comments

Comments
 (0)