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