on master help sign in
Add better explanatory doc for mfix
8f00412Harendra Kumar
src/Streamly/Internal/Data/Stream/StreamK.hs+52−14
@@ -524,26 +524,58 @@ tailPartial m = mkStream $ \st yld sng stp ->
524 yieldk _ r = foldStream st yld sng stp r
524 yieldk _ r = foldStream st yld sng stp r
525 in foldStream st yieldk single stop m
525 in foldStream st yieldk single stop m
526
526
527−-- | Iterate a lazy function `f` of the shape `m a -> t m a` until it gets
527+-- | We can define cyclic structures using @let@:
528−-- fully defined i.e. becomes independent of its argument action, then return
529−-- the resulting value of the function (`t m a`).
530--
528--
531−-- It can be used to construct a stream that uses a cyclic definition. For
529+-- >>> let (a, b) = ([1, b], head a) in (a, b)
532−-- example:
530+-- ([1,1],1)
531+--
532+-- The function @fix@ defined as:
533+--
534+-- > fix f = let x = f x in x
535+--
536+-- ensures that the argument of a function and its output refer to the same
537+-- lazy value @x@ i.e. the same location in memory. Thus @x@ can be defined
538+-- in terms of itself, creating structures with cyclic references.
539+--
540+-- >>> f ~(a, b) = ([1, b], head a)
541+-- >>> fix f
542+-- ([1,1],1)
543+--
544+-- 'Control.Monad.mfix' is essentially the same as @fix@ but for monadic
545+-- values.
546+--
547+-- Using 'mfix' for streams we can construct a stream in which each element of
548+-- the stream is defined in a cyclic fashion. The argument of the function
549+-- being fixed represents the current element of the stream which is being
550+-- returned by the stream monad. Thus, we can use the argument to construct
551+-- itself.
552+--
553+-- In the following example, the argument @action@ of the function @f@
554+-- represents the tuple @(x,y)@ returned by it in a given iteration. We define
555+-- the first element of the tuple in terms of the second.
533--
556--
534-- @
557-- @
535−-- import Streamly.Internal.Data.Stream.IsStream as S
558+-- import Streamly.Internal.Data.Stream.IsStream as Stream
536-- import System.IO.Unsafe (unsafeInterleaveIO)
559-- import System.IO.Unsafe (unsafeInterleaveIO)
537--
560--
538-- main = do
561-- main = do
539−-- S.mapM_ print $ S.mfix $ \x -> do
562+-- Stream.mapM_ print $ Stream.mfix f
540−-- a <- S.fromList [1,2]
563+--
541−-- b <- S.fromListM [return 3, unsafeInterleaveIO (fmap fst x)]
564+-- where
565+--
566+-- f action = do
567+-- let incr n act = fmap ((+n) . snd) $ unsafeInterleaveIO act
568+-- x <- Stream.fromListM [incr 1 action, incr 2 action]
569+-- y <- Stream.fromList [4,5]
542−-- return (a, b)
570+-- return (x, y)
543-- @
571-- @
544--
572--
573+-- Note: you cannot achieve this by just changing the order of the monad
574+-- statements because that would change the order in which the stream elements
575+-- are generated.
576+--
545−-- Note that the function `f` must be lazy in its argument, that's why we use
577+-- Note that the function @f@ must be lazy in its argument, that's why we use
546−-- 'unsafeInterleaveIO' because IO monad is strict.
578+-- 'unsafeInterleaveIO' on @action@ because IO monad is strict.
547--
579--
548-- /Internal/
580-- /Internal/
549
581
@@ -552,8 +584,14 @@ mfix f = mkStream $ \st yld sng stp ->
552 let single a = foldStream st yld sng stp $ a `cons` ys
584 let single a = foldStream st yld sng stp $ a `cons` ys
553 yieldk a _ = foldStream st yld sng stp $ a `cons` ys
585 yieldk a _ = foldStream st yld sng stp $ a `cons` ys
554 in foldStream st yieldk single stp xs
586 in foldStream st yieldk single stp xs
587+
588+ where
589+
590+ -- fix the head element of the stream
555− where xs = fix (f . headPartial)
591+ xs = fix (f . headPartial)
592+
593+ -- now fix the tail recursively
556− ys = mfix (tailPartial . f)
594+ ys = mfix (tailPartial . f)
557
595
558{-# INLINE init #-}
596{-# INLINE init #-}
559init :: (IsStream t, Monad m) => t m a -> m (Maybe (t m a))
597init :: (IsStream t, Monad m) => t m a -> m (Maybe (t m a))