On Thursday 14 Oct 2004 10:18 am, Simon Marlow wrote: > On 13 October 2004 16:17, Wolfgang Thaller wrote: > > We could get away with "desugaring" them to some very "unsafe" non-IO- > > bindings and having the "module init action" do something evil to > > make the IO happen in the right order... should be possible to make > > that look exactly like mdo from the outside. > > We'll end up using the unsafePerformIO hack inside the implementation > > again, so that people end up with two IORefs instead of one, but that > > should be cheap enough: > > > > foo <- someAction > > > > ... could be transformed into ... > > > > foo_var = unsafePerformIO $ newIORef (throw NonTermination) > > foo_action = someAction >>= writeIORef foo_var > > foo = unsafePerformIO $ readIORef foo > > > > ... with the appropriate NOINLINEs. > > The module init action would then make sure that foo_action gets > > invoked. > > Yes, we could do that. The fact that we're using NOCSE/NOINLINE > internally still seems very fragile, though. Oh well, perhaps we have > to live with that if we don't want the pain of a special binding type > throughout the compiler.
I'm puzzled about this idea of "module init action" in a declarative language. Perhaps, if it's desirable to have some module initialisation applied to a module if anything from it is used, the way to do this would be to have a reserved identifier specially for the purpose, like "main", but at the module level. (Though this idea still seems a bit strange to me). Also, I'm still not convinced that mdo is something I want emulated anyway, (well not if it means doing something like the above). If I've interpreted this correctly this means that someAction will always get executed, whether or not foo (or anything dependent on foo) is used elsewhere? This seems like a bad thing to me. It may be harmless enough if all it does is create a few IORefs which are then promptly garbage collected, but in some situations it could involve quite complex and expensive initialisation operations on foreign libraries (for example). Since a lot of the concerns expressed about this seem to centre around possible abuse of arbitrary IO operations in these top level constructions, maybe the problem could be addressed by insisting that a restricted monad was used, call it SafeIO say. So we'd have something like this: initIORef :: a -> SafeIO (IORef a) initMVar :: a -> SafeIO (MVar a) initEmptyMVar :: SafeIO (MVar a) liftSafeIO :: SafeIO a -> IO a The idea being that from within SafeIO you couldn't read or modify IORefs or do any other IO operation, all you could do is create new ones (or incorporate existing ones into the data structure). Wouldn't this + mdo suffice for the safe construction and initialisation of complex mutable data structures (which is probably all people want most of the time)? I guess you'd still need a get out occasionally, especially for FFI.. flakyLiftIO :: IO a -> SafeIO a :-) So at the top level you'd probably have.. myThing :: Thing myThing <- safeNewThing safeNewThing :: SafeIO Thing safeNewThing = mdo ... newThing :: IO Thing newThing = liftSafeIO safeNewThing Now that all seems so simple, I'm certain I must have overlooked something :-( Regards -- Adrian Hey _______________________________________________ Haskell mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell