[Haskell-cafe] shared oneShot IO (was top-level state proposals)

Claus Reinke claus.reinke at talk21.com
Tue May 29 18:40:11 EDT 2007


>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.




More information about the Haskell-Cafe mailing list