Adrian Hey wrote:
There's shed loads of information and semantic subtleties about pretty
much any operation you care to think of in the IO monad that isn't
communicated by it's type. All you know for sure is that it's weird,
because if it wasn't it wouldn't be in the IO monad.
So I think you're applying double standards.
Not to throw any more fuel on the fire (if at all possible), but the
reason behind this is that IO has become a sin bin for all the things
that people either don't know how to deal with, or don't care enough to
tease apart. There are many people who would like to break IO apart into
separate segments for all the different fragments of the RealWorld that
actually matter for a given purpose. To date it has not been entirely
clear how best to do this and retain a workable language.
The fact that this discussion is going on at all is, IMO, precisely
because of the sin-bin nature of IO. People have things they want to
have "global" or otherwise arbitrarily large scope, but the only notion
of a globe in Haskell is the RealWorld. Hence they throw things into IO
and then unsafePerformIO it to get it back out. There are three problems
to this approach:
(1) It's a hack and not guaranteed to work, nuff said.
(2) The RealWorld is insufficiently described to ensure any semantics
regarding *how* it holds onto the state requested of it. This problem
manifests itself in the discussion of loading the same library multiple
times, having multiple RTSes in a single OS process, etc. In those
scenarios what exactly the "RealWorld" is and how the baton is passed
among the different libraries/threads/processes/RTSes is not clearly
specified.
(3) The API language is insufficiently detailed to make demands on what
the RealWorld holds. This problem manifests itself in the argument about
whether libraries should be allowed to implicitly modify portions of the
RealWorld, or whether this requirement should be made clear in the type
signatures of the library.
As I said in the thread on [Research language vs. professional
language], I think coming up with a solution to this issue is still an
open research problem, and one I think Haskell should be exploring.
The ACIO monad has a number of nice properties and I think it should be
broken out from IO even if top-level <- aren't added to the language.
The ability to declare certain FFI calls as ACIO rather than IO is, I
think, reason enough to pursue ACIO on its own. But, ACIO does not solve
the dilemmas raised in #2 and #3. Top-level mutable state is only a
symptom of the real problem that IO and the RealWorld are insufficiently
described.
Another example where unsafePerformIO is used often is when doing RTS
introspection. Frequently, interrogating the RTS has ACIO-like
properties in that we are only interested in the RTS if a particular
thunk happens to get pulled on, and we're only interested at the time
that the pulling occurs rather than in sequence to any other actions.
The use of unsafePerformIO here seems fraught with all the same problems
as top-level mutable state. It would be nice to break out an RTS monad
(or an UnsafeGhcRTS monad, or what have you) in order to be more clear
about the exact requirements of what's going on.
But even if we break ACIO and UnsafeGhcRTS out from IO, the dilemmas
remain. To a certain extent, the dilemmas will always remain because
there will always be a frontier beyond which we don't know what's
happening: the real world exists, afterall. However, there is still room
to hope for a general approach to the problem.
One potential is to follow on the coattails of _Data Types a la Carte_.
Consider, for example, if the language provided a mechanism for users to
generate RealWorld-like non-existent tokens. Now consider removing IO[1]
and only using BS, where the thread-state parameter could be any
(co)product of RealWorld-like tokens. We could then have an overloaded
function to lift any (BS a) into a BS (a :+: b). There are some
complications with DTalC's coproducts in practice. For example, (a :+:
b) and (b :+: a) aren't considered the same type, as naturally they
can't be due to the Inl/Inr tagging. A similar approach should be able
to work however, since these tokens don't really exist at all.
Of course, once we take things to that level we're already skirting
around capability systems. Rather than using an ad-hoc approach like
this, I think it would be better to work out a theory connecting
capability systems to monad combinators, and then use that theory to
smash the sin bin of IO.
[1] Or leaving it in as a type alias for BS RealWorld.
--
Live well,
~wren
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe