> On Feb 10, 2020, at 02:18, Simon Marlow <[email protected]> wrote:
> 
>> On Mon, 10 Feb 2020 at 08:17, Simon Marlow <[email protected]> wrote:
>> 
>> Let me just say "unsafePerformIO" :)  You probably want to at least ensure 
>> that things don't crash in that case, even if you can't give a sensible 
>> semantics to what actually happens. We have a similar situation with 
>> unsafeIOToST - we can't tell you exactly what it does in general, except 
>> that it doesn't crash (I hope!).
> 
> Typo - I meant unsafeIOToSTM here.

I’ve been thinking about this. At first I figured you were probably right, and 
I decided I’d switch to a more raiseAsync-like approach. But once I started 
trying to implement it, I became less convinced it makes sense.

As its name implies, an AP_STACK is sort of like a saturated application, where 
the stack itself is the “function.” Extending the analogy, a continuation would 
be a PAP_STACK, since the stack is awaiting a value. This difference is 
significant. Suppose you write:

    let x = 1 + unsafePerformIO (shift f >>= g) in ...

If you force x, the stack will unwind to the nearest reset. When you unwind 
past x’s UPDATE_FRAME, you can’t replace the blackhole with an AP_STACK, since 
the captured slice of the stack represents the expression

    \m -> 1 + unsafePerformIO (m >>= g)

which is a function, not a thunk. The only logical interpretation of this 
situation is that x is a thunk quite like

    let y = 1 + error "bang"

except that it doesn’t just abort to the enclosing reset frame when forced, it 
actually composes the captured continuation with the upper frames of the 
current stack and passes the composed continuation to f to resume computation. 
That’s an awful lot of trouble given the resulting semantics is going to be 
unpredictable, anyway!

I should be clear that I do not intend shift/reset to have any safe interface 
in IO directly. Even if you could make it type safe, it would break all kinds 
of code that manages resources using bracket. Rather, I have built a library 
that defines a totally separate Eff monad, and that monad uses the primops 
directly, wrapped in a safe interface. It isn’t possible for a user to screw 
things up using unsafePerformIO because there is no way to call shift from IO 
(unless you wrap shift# in IO yourself, but then I think you can be expected to 
know what you’re getting yourself into). So without mucking about with 
GHC.Exts, you still can’t get segfaults.

Alexis
_______________________________________________
ghc-devs mailing list
[email protected]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

Reply via email to