Skip to content

Commit c644dd4

Browse files
authored
Document internals of liftA2 (#715)
- Renamed aptyMiddle to liftA2Middle and some of its parameters - Document invariants and meaning of parameters. - A short note about sharing related to ffirstx, flastx, and f.
1 parent 2f4462f commit c644dd4

File tree

1 file changed

+107
-44
lines changed

1 file changed

+107
-44
lines changed

containers/src/Data/Sequence/Internal.hs

Lines changed: 107 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ apSeq fs xs@(Seq xsFT) = case viewl fs of
531531
RigidFull r@(Rigid s pr _m sf) -> Seq $
532532
Deep (s * length fs)
533533
(fmap (fmap firstf) (nodeToDigit pr))
534-
(aptyMiddle (fmap firstf) (fmap lastf) fmap fs''FT r)
534+
(liftA2Middle (fmap firstf) (fmap lastf) fmap fs''FT r)
535535
(fmap (fmap lastf) (nodeToDigit sf))
536536
{-# NOINLINE [1] apSeq #-}
537537

@@ -591,7 +591,7 @@ liftA2Seq f xs ys@(Seq ysFT) = case viewl xs of
591591
RigidFull r@(Rigid s pr _m sf) -> Seq $
592592
Deep (s * length xs)
593593
(fmap (fmap (f firstx)) (nodeToDigit pr))
594-
(aptyMiddle (fmap (f firstx)) (fmap (f lastx)) (lift_elem f) xs''FT r)
594+
(liftA2Middle (fmap (f firstx)) (fmap (f lastx)) (lift_elem f) xs''FT r)
595595
(fmap (fmap (f lastx)) (nodeToDigit sf))
596596
where
597597
lift_elem :: (a -> b -> c) -> a -> Elem b -> Elem c
@@ -638,65 +638,128 @@ data Digit12 a = One12 a | Two12 a a
638638
-- digit of a 'Rigid' tree.
639639
type Digit23 a = Node a
640640

641-
-- | 'aptyMiddle' does most of the hard work of computing @fs<*>xs@. It
641+
-- | 'liftA2Middle' does most of the hard work of computing @liftA2 f xs ys@. It
642642
-- produces the center part of a finger tree, with a prefix corresponding to
643-
-- the prefix of @xs@ and a suffix corresponding to the suffix of @xs@ omitted;
643+
-- the first element of @xs@ and a suffix corresponding to its last element omitted;
644644
-- the missing suffix and prefix are added by the caller. For the recursive
645645
-- call, it squashes the prefix and the suffix into the center tree. Once it
646646
-- gets to the bottom, it turns the tree into a 2-3 tree, applies 'mapMulFT' to
647647
-- produce the main body, and glues all the pieces together.
648648
--
649-
-- @map23@ itself is a bit horrifying because of the nested types involved. Its
649+
-- @f@ itself is a bit horrifying because of the nested types involved. Its
650650
-- job is to map over the *elements* of a 2-3 tree, rather than the subtrees.
651651
-- If we used a higher-order nested type with MPTC, we could probably use a
652-
-- class, but as it is we have to build up @map23@ explicitly through the
652+
-- class, but as it is we have to build up @f@ explicitly through the
653653
-- recursion.
654-
aptyMiddle
655-
:: (b -> c)
656-
-> (b -> c)
657-
-> (a -> b -> c)
658-
-> FingerTree (Elem a)
659-
-> Rigid b
660-
-> FingerTree (Node c)
654+
--
655+
-- === Description of parameters
656+
--
657+
-- ==== Types
658+
--
659+
-- @a@ remains constant through recursive calls (in the @DeepTh@ case),
660+
-- while @b@ and @c@ do not: 'liftAMiddle' calls itself at types @Node b@ and
661+
-- @Node c@.
662+
--
663+
-- ==== Values
664+
--
665+
-- 'liftA2Middle' is used when the original @xs :: Sequence a@ has at
666+
-- least two elements, so it can be decomposed by taking off the first and last
667+
-- elements:
668+
--
669+
-- > xs = firstx <: midxs :> lastx
670+
--
671+
-- - the first two arguments @ffirstx, flastx :: b -> c@ are equal to
672+
-- @f firstx@ and @f lastx@, where @f :: a -> b -> c@ is the third argument.
673+
-- This ensures sharing when @f@ computes some data upon being partially
674+
-- applied to its first argument. The way @f@ gets accumulated also ensures
675+
-- sharing for the middle section.
676+
--
677+
-- - the fourth argument is the middle part @midxs@, always constant.
678+
--
679+
-- - the last argument, a tuple of type @Rigid b@, holds all the elements of
680+
-- @ys@, in three parts: a middle part around which the recursion is
681+
-- structured, surrounded by a prefix and a suffix that accumulate
682+
-- elements on the side as we walk down the middle.
683+
--
684+
-- === Invariants
685+
--
686+
-- > 1. Viewing the various trees as the lists they represent
687+
-- > (the types of the toList functions are given a few paragraphs below):
688+
-- >
689+
-- > toListFTN result
690+
-- > = (ffirstx <$> (toListThinN m ++ toListD sf))
691+
-- > ++ (f <$> toListFTE midxs <*> (toListD pr ++ toListThinN m ++ toListD sf))
692+
-- > ++ (flastx <$> (toListD pr ++ toListThinN m))
693+
-- >
694+
-- > 2. s = size m + size pr + size sf
695+
-- >
696+
-- > 3. size (ffirstx y) = size (flastx y) = size (f x y) = size y
697+
-- > for any (x :: a) (y :: b)
698+
--
699+
-- Projecting invariant 1 on sizes, using 2 and 3 to simplify, we have the
700+
-- following corollary.
701+
-- It is weaker than invariant 1, but it may be easier to keep track of.
702+
--
703+
-- > 1a. size result = s * (size midxs + 1) + size m
704+
--
705+
-- In invariant 1, the types of the auxiliary functions are as follows
706+
-- for reference:
707+
--
708+
-- > toListFTE :: FingerTree (Elem a) -> [a]
709+
-- > toListFTN :: FingerTree (Node c) -> [c]
710+
-- > toListThinN :: Thin (Node b) -> [b]
711+
-- > toListD :: Digit12 b -> [b]
712+
liftA2Middle
713+
:: (b -> c) -- ^ @ffirstx@
714+
-> (b -> c) -- ^ @flastx@
715+
-> (a -> b -> c) -- ^ @f@
716+
-> FingerTree (Elem a) -- ^ @midxs@
717+
-> Rigid b -- ^ @Rigid s pr m sf@ (@pr@: prefix, @sf@: suffix)
718+
-> FingerTree (Node c)
661719

662720
-- Not at the bottom yet
663721

664-
aptyMiddle firstf
665-
lastf
666-
map23
667-
fs
668-
(Rigid s pr (DeepTh sm prm mm sfm) sf)
669-
= Deep (sm + s * (size fs + 1)) -- note: sm = s - size pr - size sf
670-
(fmap (fmap firstf) (digit12ToDigit prm))
671-
(aptyMiddle (fmap firstf)
672-
(fmap lastf)
673-
(fmap . map23)
674-
fs
675-
(Rigid s (squashL pr prm) mm (squashR sfm sf)))
676-
(fmap (fmap lastf) (digit12ToDigit sfm))
722+
liftA2Middle
723+
ffirstx
724+
flastx
725+
f
726+
midxs
727+
(Rigid s pr (DeepTh sm prm mm sfm) sf)
728+
-- note: size (DeepTh sm pr mm sfm) = sm = size pr + size mm + size sfm
729+
= Deep (sm + s * (size midxs + 1)) -- note: sm = s - size pr - size sf
730+
(fmap (fmap ffirstx) (digit12ToDigit prm))
731+
(liftA2Middle
732+
(fmap ffirstx)
733+
(fmap flastx)
734+
(fmap . f)
735+
midxs
736+
(Rigid s (squashL pr prm) mm (squashR sfm sf)))
737+
(fmap (fmap flastx) (digit12ToDigit sfm))
677738

678739
-- At the bottom
679740

680-
aptyMiddle firstf
681-
lastf
682-
map23
683-
fs
684-
(Rigid s pr EmptyTh sf)
685-
= deep
686-
(One (fmap firstf sf))
687-
(mapMulFT s (\(Elem f) -> fmap (fmap (map23 f)) converted) fs)
688-
(One (fmap lastf pr))
741+
liftA2Middle
742+
ffirstx
743+
flastx
744+
f
745+
midxs
746+
(Rigid s pr EmptyTh sf)
747+
= deep
748+
(One (fmap ffirstx sf))
749+
(mapMulFT s (\(Elem x) -> fmap (fmap (f x)) converted) midxs)
750+
(One (fmap flastx pr))
689751
where converted = node2 pr sf
690752

691-
aptyMiddle firstf
692-
lastf
693-
map23
694-
fs
695-
(Rigid s pr (SingleTh q) sf)
696-
= deep
697-
(Two (fmap firstf q) (fmap firstf sf))
698-
(mapMulFT s (\(Elem f) -> fmap (fmap (map23 f)) converted) fs)
699-
(Two (fmap lastf pr) (fmap lastf q))
753+
liftA2Middle
754+
ffirstx
755+
flastx
756+
f
757+
midxs
758+
(Rigid s pr (SingleTh q) sf)
759+
= deep
760+
(Two (fmap ffirstx q) (fmap ffirstx sf))
761+
(mapMulFT s (\(Elem x) -> fmap (fmap (f x)) converted) midxs)
762+
(Two (fmap flastx pr) (fmap flastx q))
700763
where converted = node3 pr q sf
701764

702765
digit12ToDigit :: Digit12 a -> Digit a

0 commit comments

Comments
 (0)