Typesafe MRef with a regular monad

Derek Elkins ddarius@hotpop.com
Wed, 4 Jun 2003 20:07:34 -0400


On Wed, 04 Jun 2003 15:19:53 -0700
Ashley Yakeley <ashley@semantic.org> wrote:

> In article <200306042005.h54K5mG0014203@adric.fnmoc.navy.mil>,
>  oleg@pobox.com wrote:
> 
> > Ashley Yakeley wrote:
> > ] ] Is it possible to actually implement a working instance of
> > RefMonad in ] ] Haskell, without making use of a built-in monad like
> > IO or ST?  
> > 
> > ] You certainly wouldn't be able to do this for any monad M which
> > had:
> > 
> > ]   performM :: forall a. M a -> a;
> > 
> > ] ...because it wouldn't be type-safe: you'd be able to construct
> > coerce ] :: a -> b just as you can with unsafePerformIO.
> >  
> > Fortunately, that doesn't seem to be the case.
> 
> That's only because you've failed to do the difficult part: implement 
> newRef. Your monadic solution has a statically typed/sized store: I'd 
> say it doesn't properly count as a "heap" since you can't heap new
> stuff on it.

I agree, if I knew I'd have 5 components before I could just use a 5
tuple and a State monad.  I'd have to look back over the other heap
stuff to see what it provides type-wise, but (at least the "new" monad
version) seems to miss the point.

> The original problem was to create an instance of 
> 
>   class Monad m => RefMonad m r | m -> r where
>      newRef :: a -> m (r a)
>      readRef :: r a -> m a
>      writeRef :: r a -> a -> m ()
> 
> without making use of IO or ST. Given some M and R that have
> 
>   instance RefMonad M R
>   performM :: forall a. M a -> a

M = (forall s.ST s)
R = STRef s

e.g. runST :: (forall s.ST s a) -> a

you can use the same trick for your own RefMonad.  I'm not sure if this
will work with RefMonad exactly.  If ST/STRef can be made an instance of
RefMonad without any trouble though, then I believe it should work.

> one can write this:
> 
>   coerce :: forall a b. a -> b;
>   coerce a = let
>     {
>     ref = performM (newRef Nothing);
>     } in performM (do
>     {
>     writeRef ref (Just a);
>     mb <- readRef ref;
>     case mb of {Just b -> return b;};
>     });

I was having fun with
coerce :: a -> b
coerce x = unsafePerformIO (writeIORef ref x >> readIORef ref)
    where ref = unsafePerformIO (newIORef undefined)

last night, some fun examples (using GHCi 5.04.3),
data Foo a = Bar | Baz a (Foo a)

coerce 5 :: Maybe Int ==> Nothing
coerce 'a' :: Int ==> 97
coerce [1..3] :: Foo Integer ==> (Baz 1 (Baz 2 (Baz 3 Bar)))
coerce [4] :: Maybe Integer ==> Just 4

I need to reread the GHC internals paper, I want to see if I can get one
like
(coerce something :: (sometype -> someothertype)) someotherthing