Type of newForeignPtr & addForeignPtrFinalizer

Simon Marlow simonmar@microsoft.com
Tue, 23 Jul 2002 15:09:35 +0100

> On Monday 22 July 2002 12:33 pm, Simon Marlow wrote:
> > > The second seems to require this bit of weirdness..
> > >  myNewForeignPtr :: Ptr a -> (ForeignPtr a -> IO ()) -> IO
> > > (ForeignPtr a)
> > >  myNewForeignPtr p fin =3D do
> > >    newfp  <- newForeignPtr p (return ())
> > >    addForeignPtrFinalizer newfp (fin newfp)
> > >    return newfp
> >
> > You can do this more easily using fixIO:
> >
> >    myNewForeignPtr p fin =3D do
> > 	fixIO (\fp -> newForeignPtr p (fin fp))
> Thanks, neat (I think:-). I wonder if I might indulge myself with
> another stupid question related to this, that is, why make the
> distinction between Ptr and ForeignPtr at all?
> By definition a ForeignPtr has a non-zero number of finalisers
> and Ptr has no finalisers. Couldn't you just allow ForeignPtr's
> to have no finalisers and dispense with Ptr alltogether? Then
> you could just add finalisers as required rather than converting
> between types. It seems that when making a foreign binding you have
> to make what seems (to me) an arbitrary choice between Ptr and
> ForeignPtr arguments. I don't really understand the reason for
> this extra complexity.

There's a difference in representation between Ptr & ForeignPtr, which
means that ForeignPtr might be significantly less efficient than Ptr.

ForeignPtr is represented as (ForeignPtr ForeignPtr#), where ForeignPtr#
is a real heap object of size 2.  It needs to be a heap object so that
the garbage collector can keep track of it and tell when it needs to be

Ptr on the other hand is represented as (Ptr Addr#) where Addr# is just
an unboxed address - it doesn't live on the heap, so the garbage
collector can't track it.  You might ask why we can't just put the
finalizer on the boxed version (the (Ptr Addr#)) - and the answer is
that the compiler might optimise away the box, which might cause the
finalizer to run earlier than you intended.  The compiler can't optimise
away the box on a ForeignPtr#, because as far as the compiler is
concerned a ForeignPtr# is a primitive indivisible object.  It's quite a
subtle point, but an important one.

My advice is to use Ptr unless you really really need ForeignPtr.  There
are very few guarantees about the behaviour of finalizers - about the
only guarantee you've got is that the finalizer will run "eventually",
so it's difficult to provide any guarantees about memory use if you rely
on finalizers to free memory.