I was wondering why, since IO is an instance of MonadFix [1], and therefore of ArrowLoop (Kleisli m), and since "The loop operator expresses computations in which an output value is fed back as input, even though the computation occurs only once." [2], the MonadFix or ArrowLoop class (through use of mfix or loop, respectively) doesn't appear in anyone's suggestion, where the top-level state was the thing looped over.

oh, but it does!-) see 'proposal 2: top-level <-', and especially John
Meacham's elaboration. 'mdo' is recursive do-notation, based on 'MonadFix', which for 'IO' is based on 'fixIO' (John's email gives
references). (*)

the problem with that is what happens to multiple bindings: according
to the usual 'mdo'-translation, they are interpreted as a *sequence*,
so order matters, which is kind of a big change for top-level bindings
spread over a hierarchy of modules. as is the potential for allowing arbitrary IO actions to be performed as part of evaluating a set of recursive bindings and imports. see the wiki page for some of the issues and proposed workarounds.

what is different about the variation i proposed is that the only thing
that is merged into the evaluation of top-level bindings is the creation
of some mutable variables, which are not even explicitly accessible,
but are only used behind the scenes, to realise sharing. this is a kind
of effect for which the ordering is immaterial, and since this effect
does not depend on the actual IO action being shared, we do not
need to know anything about that IO action either to guarantee
that we can order bindings any way we like.

and since the actual IO action being shared is not performed unsafely,
it remains in the IO monad, and has to be invoked explicitly, so this variation should also be safer (no side-effects due to mere module
import, for instance).

it might still make sense to interpret '=<'-bindings via 'mdo', to allow
for mutual recursion in the bindings. but since all top-level bindings
are now either of the form 'var =< io', where 'io' will not be executed
until 'var' is invoked within the 'IO' monad, or of the form 'let var = expr',
where no 'IO' effects are involved, the ordering of the bindings does
no longer matter. as i think it should be.

hth,
claus

(*)

Or is this more or less what is going on in the function .. oneShot :: IO a -> ACIO (IO a) ..
but without explicitly using the MonadFix or ArrowLoop classes?

oneShot, mkOnceIO, and fixIO, have an implementation technique
in common, which is to allocate space for a result, then executing
some code to figure out what that result might be. by passing the
reference to where the result will be stored to the code computing
it, cyclic representations of recursive structures can be constructed.


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to