
Can anyone explain why this leaks space: main :: IO () main = let loop () = return () *> loop () in loop () whilst this doesn't: main :: IO () main = let loop = return () *> loop in loop I'm stumped.

On Fri, Nov 04, 2016 at 02:44:00PM +0000, Tom Ellis wrote:
Can anyone explain why this leaks space:
main :: IO () main = let loop () = return () *> loop () in loop ()
whilst this doesn't:
main :: IO () main = let loop = return () *> loop in loop
More strangely, the space leak only appears in ghci (and runhaskell). When compiled there is no such space leak. Tom

Interesting. We generally have
pure x = IO (\ s -> (# s, x #))
IO m *> n = IO (\ s -> case m s of (# new_s, a #) -> unIO ((const n) a) new_s)
so
main = let loop () = IO (\s -> (# s, () #)) *> loop () in loop ()
====
let loop () = IO (\s -> case (\s' -> (# s', () #)) s of (# new_s, a #)
-> unIO ((const (loop ()) a) new_s) in loop ()
====
let loop () = IO (\s -> case (# s, () #) of (# new_s, a #) -> unIO
(loop ()) new_s) in loop ()
====
let loop () = IO (\s -> unIO (loop ()) s) in loop ()
My only guess is that *somehow* this is getting memoized and expanding
under the lambda to
let loop () = IO (\s -> unIO (IO (\s -> unIO ......)) s) in loop ()
but I know nothing about GHCi internals, so that seems as weird to me
as it does to you.
On Fri, Nov 4, 2016 at 10:58 AM, Tom Ellis
On Fri, Nov 04, 2016 at 02:44:00PM +0000, Tom Ellis wrote:
Can anyone explain why this leaks space:
main :: IO () main = let loop () = return () *> loop () in loop ()
whilst this doesn't:
main :: IO () main = let loop = return () *> loop in loop
More strangely, the space leak only appears in ghci (and runhaskell). When compiled there is no such space leak.
Tom _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

On Mon, Nov 7, 2016 at 4:02 PM, David Feuer
IO m *> n = IO (\ s -> case m s of (# new_s, a #) -> unIO ((const n) a) new_s)
From looking at the Applicative source, this appears to be correct. Why don't we use thenIO instead, which elides the "const" and has the form
thenIO https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#then... (IO m https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#loca...) k https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#loca... = IO (\ s https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#loca... -> case m https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#loca... s https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#loca... of (# new_s https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#loca..., _ #) -> unIO https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#unIO k https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#loca... new_s https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#loca...) It doesn't look like thenIO is used or exported, which makes me think someone either forgot about it or there's some good reason that isn't documented. Obviously if things go as planned, the const gets reduced away at compile time, but it seems like if we have thenIO sitting there in the source we might as well use it and not have to worry about compile-time eta-reduction of (\_ -> k). Will
participants (3)
-
David Feuer
-
Tom Ellis
-
William Yager