[Haskell-cafe] Dynamic types through unsafeCoerce

Udo Stenzel u.stenzel at web.de
Tue Dec 12 19:25:32 EST 2006

Alfonso Acosta wrote:
> If anyone finds a way of implementing something equivalent to this code 
> without unsafeCoerce#  and ...
> * Not changing chooseDesc or finding an equivalent
> * Not splitting  or changing Descriptor type (I already found an
> equivalent way which uses existentials and in which the type is
> splitted in two)

Well, all you need to do is to throw out your OO-Think (and with it
Typeable and casts) and you'll realize what you're actually doing here:
you're passing functions.  Say so and everything comes naturally.
(Warning:  untested code.)

-- ----------
type Descriptor = InstanceInitData -> Runner
	-- a function!  Who would have thought it?

newtype Runner = R { run :: IO Runner }
	-- could be a plain (IO Runner) instead of a newtype, if only it
	-- weren't a recursive type

descInt, descChar :: Descriptor
descInt = const (runInt 1)
    runInt n = R (do print n ; return . runInt $ n*2)

descChar = const (runChar 'a')
    runChar c = R (do print c ; return . runChar $ succ hd)

descList :: [Descriptor] 	-- homogenous!
descList = [ descInt, descChar ]
-- ---------

Finished!  Look Ma, no existentials, no Typeable, no wrappers, even the
types have become simple!  Descriptor doesn't even need a type argument
anymore, and indeed, why should it?  Its purpose is exactly to _hide_ an
Int/Char/whatever, not to expose it.

Okay, I cheated a bit: I _did_ split Descriptor in two.  That feels more
right anyway, since 'instantiate' is only going to be called once (I
think) and before 'instantiate' is called, there is no meaningful 'run'
function anyway (and of that I'm sure).  If you don't like that, feel
free to fuse Descriptor and Runner back into one record, but then you
need to think about what to initialize 'run' to.  In fact, even in
OO-Think you should be passing a constructor, err... factory to C land,
not a half-baked object.

Rest is wrappers to be able to call the above from the netherworlds,
"foreign export" statements snipped.  Oh, and lots of 'freeStablePtr'
are also missing.  Adding them will be left as a training exercise :)

-- ---------
chooseDesc :: Int -> IO (StablePtr Descriptor)
chooseDesc n = newStablePtr (descList !! n)
cInstantiate ::
  StablePtr Descriptor -> InstanceInitData -> IO (StablePtr Runner)
cInstantiate ptr iid = do desc <- deRefStablePtr ptr
                          newStablePtr . desc $ iid
cRun :: StablePtr Runner-> IO (StablePtr Runner)
cRun hdptr = deRefStablePtr hdptr >>= run >>= newStablePtr
-- ---------

"I've seen it.  It's rubbish."
	-- Marvin the Paranoid Android
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
Url : http://www.haskell.org/pipermail/haskell-cafe/attachments/20061213/622e52d9/attachment-0001.bin

More information about the Haskell-Cafe mailing list