[prev in list] [next in list] [prev in thread] [next in thread] 

List:       haskell-cafe
Subject:    Why is this program leaking memory ?
From:       Ahn Ki-yung <kyagrd () bawi ! org>
Date:       2003-05-28 14:18:37
Message-ID: 3ED4C53D.4000505 () bawi ! org
[Download RAW message or body]

I'm playing with the lazy abstract machine of 1997 Sestoft's paper.
I implemented this with haskell using ghc 5.04.3.

I lambda lifted the original input expression to prevent
memory leak of "Lazy Abstarc Machine", and it works fine.
I tested with the leaky program example of the Sestoft 97 paper.

[kyagrd@OBIWAN transform]$ cat test.txt
let ff = \n.let i=\x.x in ff i
in ff ff

I printed the 4 tuples (Heap, Exp, Env, Stack) each step.
It goes on and on like this.

[kyagrd@OBIWAN transform]$ ./main.exe < test.txt
let "ff"=\"n".let "i"=\"x"."x" in "ff" "i" in "ff" "ff"
let 1=\2.let 3=\4.4 in 1 3 in 1 1
let -1=\2.-1 -3 -3=\4.4 in -1 -1
let -1=\2.-1 -3 -3=\4.4 in -1 -1
let 0=\0.1 2 0=\0.0 in 0 0
([],let 0=\0.1 2 0=\0.0 in 0 0,[],[])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],0 0,[2,1],[])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],0,[2,1],[2])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],\0.1 2,[2,1],[2])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],1 2,[2,2,1],[])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],1,[2,2,1],[1])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],\0.1 2,[2,1],[1])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],1 2,[1,2,1],[])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],1,[1,2,1],[1])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],\0.1 2,[2,1],[1])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],1 2,[1,2,1],[])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],1,[1,2,1],[1])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],\0.1 2,[2,1],[1])
([(2,(\0.1 2,[2,1])),(1,(\0.0,[2,1]))],1 2,[1,2,1],[])
...


But Strangely the Haskell heap memory leaks if I omit printing
every step but only print the result.

[kyagrd@OBIWAN transform]$ ./main.exe < test.txt
let "ff"=\"n".let "i"=\"x"."x" in "ff" "i" in "ff" "ff"
let 1=\2.let 3=\4.4 in 1 3 in 1 1
let -1=\2.-1 -3 -3=\4.4 in -1 -1
let -1=\2.-1 -3 -3=\4.4 in -1 -1
let 0=\0.1 2 0=\0.0 in 0 0
c:\MyDoc\iFolder\kyagrd\LAZY\transform\main.exe: fatal error:
RTS exhausted max heap size (268435456 bytes)


I attach my source code except for the parser and lexer stuff.
I switched between two main' and main fucntion.

Why, in the Main module, "printNreduce" do not leak but
while "printeval" leaks ? Can't understand this behavior.


["Main.hs" (text/plain)]

module Main where

import Syntax
import Parser
import Lazyeval
import MonadST

printeval q = if q'==q then print q' else printeval q'
	where q' = reduce q

printNreduce q = if q'==q then print q' else (print q >> printNreduce q')
	where q' = reduce q

-- print every step does not leak
main = do
	s <- getContents
	let e = parser s
	e' <- printNpreprocess e
	printNreduce ([],e',[],[])

-- print only result leak !!
main' = do
	s <- getContents
	let e = parser s
	e' <- printNpreprocess e
	printeval ([],e',[],[])

printNpreprocess e =
	print e >> print e1 >> print e2 >> print e3 >> print e' >> return e'
	where
	((ns,[]),e1) = evalST ([1..],[]) (uniqueify e)
	e2 = lambdalift negate e1
	(ns3,e3) = evalST ns (normalize e2)
	e' = bruijnize e3

["Syntax.hs" (text/plain)]

{-
Syntax.hs

preprocessing of lazy language for evaluation

Ahn Ki-yung
-}

module Syntax where

import MonadST
import List

data Lambda id
	= Var id
	| App (Lambda id) (Lambda id)
	| Lam id (Lambda id)
	| Let [(id,Lambda id)] (Lambda id)
--	deriving (Eq, Ord)
	deriving (Eq, Ord, Read)

instance Show a => Show (Lambda a) where
	show (Var s) = show s
	show (Lam s e) = '\\':show s ++ '.':show e
	show (App e e') =
		showParenExpr e ++ ' ' : showParenExpr e'
		where
			showParenExpr e@(Var s) = show e
			showParenExpr e = '(':show e++")"
	show (Let h e) = "let"
		++ concat [' ':show s++'=':show e|(s,e)<-h]
		++ " in " ++ show e

instance Functor Lambda where
	fmap f (Var x) = Var (f x)
	fmap f (App e e') = App (fmap f e) (fmap f e')
	fmap f (Lam x e) = Lam (f x) (fmap f e)
	fmap f (Let ds e) = Let [(f x,fmap f e)|(x,e)<-ds] (fmap f e)

transform (getId,putId,popId) = trans
	where
	newId x = putId x >> getId x
	trans (Var x) = do { x'<-getId x; return (Var x') }
	trans (App e e1) = do { e'<-trans e; e1'<-trans e1; return (App e' e1') }
	trans (Lam x e) =
		do { x'<-newId x; e'<-trans e; popId x; return (Lam x' e') }
	trans (Let ds e) = do
		let (xs,es) = unzip ds
		xs'<-mapM newId xs
		es'<-mapM trans es
		let ds' = zip xs' es'
		e'<-trans e
		mapM popId xs
		return (Let ds' e')

uniqueify :: (Eq a, Eq b) => Lambda a -> StateTrans ([b],[(a,b)]) (Lambda b)
uniqueify = transform (getId,putId,popId) where
	getId x = do
		(_,l) <- readST
		let Just (_,n) = find ((x==).fst) l
		return n
	putId x = do
		(n:ns,l) <- readST
		writeST (ns,(x,n):l)
	popId x = do
		(ns,l) <- readST
		writeST (ns,deleteBy (\p-> \q->fst p==fst q) (x,head ns) l)

normalize :: Lambda a -> StateTrans [a] (Lambda a)
normalize = normExpr where
	newvar = do
		(x:xs) <- readST
		writeST xs
		return x
	normExpr (Var x) = return (Var x)
	normExpr (Lam x e) = do
		e' <- normExpr e
		return (Lam x e')
	normExpr (App e (Var x)) = do
		e' <- normExpr e
		return (App e' (Var x))
	normExpr (App e1 e2) = do
		x <- newvar
		e1' <- normExpr e1
		e2' <- normExpr e2
		return (Let [(x,e2')] (App e1' (Var x)))
	normExpr (Let ds e) = do
		let (vs,es) = unzip ds
		es' <- mapM normExpr es
		let ds' = zip vs es'
		e' <- normExpr e
		return (Let ds' e')


(f,g)@@(x,y) = (f x, g y)

-- assumes uniquified normalized e
lambdalift topv e = Let ds (subsfree [] topv d' e')
	where
	d' = delcombs [] d
	ds = [ (topv x, foldr Lam (subsfree fv topv d' e) fv) | (x,fv,e)<-d' ]
	(d,fv,e') = llift e
	delcombs combs d =
		if combs/=combs'
		then delcombs combs' [(x,fv\\combs',e) | (x,fv,e)<-d]
		else d
		where combs' = [x | (x,[],_)<-d]

-- assumes uniquified and no lets -- done llift
subsfree bv topv d e = subs e
	where
	var' = Var . topv
	subs (Var x) = case find (\(y,_,_)->x==y && not (elem x bv)) d of
		Just (_,fv,_) -> foldl App (var' x) (map var' fv)
		_ -> Var x
	subs (App e e') = App (subs e) (subs e')
	subs (Lam x e) = Lam x (subs e)

-- assumes uniquified
llift (Var x) = ([], [x], Var x)
llift (App e e') = (d1++d2, union fv1 fv2, App e1 e2)
	where
	(d1,fv1,e1) = llift e
	(d2,fv2,e2) = llift e'
llift (Lam x e) = (d, fv\\[x], Lam x e') where (d, fv, e') = llift e
llift (Let ds e) = (dd++d', fv, e')
	where
	(d, fv, e') = llift e
	xds = map ((id,llift)@@) ds
	dd = [ (x, fv\\[x], e) | (x,(d,fv,e))<-xds ]
	d' = foldr (++) d [d | (_,(d,_,_))<-xds]

bruijnize e = bruijn [] e

elemIndex' x xs = (\(Just i)->i) $ elemIndex x xs

bruijn xs (Var x) = Var (elemIndex' x xs)
bruijn xs (Lam x e) = Lam 0 (bruijn (x:xs) e)
bruijn xs (App e e') = App (bruijn xs e) (bruijn xs e')
bruijn xs (Let ds e) = Let [(0,bruijn xs' e) | e<-es] (bruijn xs' e)
	where
	(vs,es) = unzip ds
	xs' = vs ++ xs

["Lazyeval.hs" (text/plain)]

module Lazyeval where

import Syntax
import List

data StackElem = Update Int | Point Int deriving Eq

instance Show StackElem where
	show (Update i) = '#':show i
	show (Point i) = show i

getHeap p = (\(Just x)->snd x) . find ((p==).fst)
setHeap t@(p,e) =
	insertBy (mapF2 fst $ flip compare) t . deleteBy (mapF2 fst (==)) t
		where mapF2 g f2 x y = f2 (g x) (g y)

reduce (h,App e (Var i),env,s) = (h,e,env,Point(env!!i):s)
reduce (h,Lam _ e,env,Point p:s) = (h,e,p:env,s)
reduce (h,Var i,env,s) = (h,e',env',s')
	where
		s' = case e' of Lam _ _ ->s; _->Update p:s
		(e',env') = getHeap p h
		p = env!!i
reduce (h,Lam x e,env,Update p:s) = (setHeap (p,(Lam x e,env)) h,Lam x e,env,s)
reduce (h,Let ds e,env,s) = (h',e,env',s)
	where
		es = snd (unzip ds)
		h' = newhs ++ h
		env' = newps ++ env
		newhs = zip newps [(e,env')|e<-es]
		newps = take n [m+n,m+n-1..]
		m = if null h then 0 else (head . fst . unzip) h
		n = length es
reduce q = q

fix f x = if x'==x then x else fix f x' where x' = f x

eval = fix reduce

["MonadST.hs" (text/plain)]

module MonadST (readST, writeST, applyST, evalST, valueST, stateST, StateTrans) where

data StateTrans s a = ST { st :: s -> (s,a) }

instance Monad (StateTrans a) where
	return x = ST (\s -> (s, x))
	m >>= f = ST (\s -> let (s',x) = st m s in st (f x) s')

readST = ST (\s -> (s, s))
writeST s' = ST (\s -> (s', ()))
applyST f = ST (\s -> (f s, ()))

evalST s m = st m s
valueST s = snd . evalST s
stateST s = fst . evalST s


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic