Rattus-0.2: test/MemoryLeak.hs
{-# LANGUAGE TypeOperators #-}
module Main (module Main) where
import Rattus
import Rattus.Stream
import Rattus.ToHaskell
import qualified Prelude
import Prelude hiding ((<*>), map)
{-# ANN module Rattus #-}
scan3 :: (Stable a) => Box(a -> a -> a) -> Box (a -> Bool) -> a -> Str a -> Str a
scan3 f p acc (a ::: as) = (if unbox p a then acc else a)
::: (delay (scan3 f p acc') <*> as)
where acc' = unbox f acc a
-- Unless the strictification transformation is applied this function
-- will leak memory.
test1 :: Int -> Str (Int) -> Str (Int)
test1 = scan3 (box (+)) (box (== 0))
-- If we Haskell's (lazy) pair types, we get a memory leak:
type Lazy a = ((),a)
leakyLazy :: Str (Lazy Int) -> Str (Lazy Int)
leakyLazy ((_,x):::xs) = ((),1) ::: delay (leakyLazy (fmap ((+) x) (hd (adv xs)) ::: (tl (adv xs))))
-- If we use a strict pair type, we avoid the memory leak
type Strict a = (():*a)
leakyStrict :: Str (Strict Int) -> Str (Strict Int)
leakyStrict ((_:*x):::xs) = (():*11) ::: delay (leakyStrict (fmap ((+) x) (hd (adv xs)) ::: (tl (adv xs))))
-- Unless the strictification transformation is applied this function
-- will leak memory.
leaky :: Str (Int) -> Str (Int)
leaky (x:::xs) = 1 ::: delay (leaky ((x + hd (adv xs)) ::: (tl (adv xs))))
buffer :: Stable a => Str a -> Str (List a)
buffer = scan (box (flip (:!))) Nil
{-# ANN recurse NotRattus #-}
recurse 0 (x : _) = print x
recurse n (_ : xs) = recurse (n-1) xs
recurse _ [] = putStrLn "the impossible happened: stream terminated"
{-# ANN main NotRattus #-}
main = do
let x = fromStr $ test1 1 (toStr [1..])
recurse 10000000 x
let x = fromStr $ leaky (toStr $ [1..])
recurse 10000000 x
let x = fromStr $ leakyStrict (toStr $ Prelude.map (\ x-> (():*x)) [1..])
recurse 10000000 x
-- This will leak du to lazy data structure
let x = fromStr $ leakyLazy (toStr $ Prelude.map (\ x-> ((),x)) [1..])
recurse 10000000 x
-- -- for comparison the Haskell code below does leak
-- let x = scan2 (+) (1::Int) [1,1..]
-- recurse 10000000 x
-- {-# ANN scan2 NotRattus #-}
-- scan2 :: (Int -> Int -> Int) -> Int -> [Int] -> [Int]
-- scan2 f acc (a : as) = (if a == 0 then acc else 0) : scan2 f (f acc a) as