[jhc] darcs patch: try to abstract IO a bit

David Roundy daveroundy at gmail.com
Mon Sep 7 21:11:38 EDT 2009


On Mon, Sep 7, 2009 at 6:40 PM, John Meacham<john at repetae.net> wrote:
> On Mon, Sep 07, 2009 at 07:26:26AM -0400, David Roundy wrote:
>> Hmm.  After experimenting a bit more, I think I'm going to give up on
>> this particular project.  I don't see a way to introduce a new IO
>> without duplicating most/all of base and jhc.  And even if I were to
>> do that, it doesn't look like the result would work with FFI without
>> requiring users to manually wrap FFI calls into IO from Jhc.Prim.IO.
>
> Yeah, it would be tricky. The best way I could think of would be to have
> multiple versions of just the jhc library, and link base against
> different versions of it. it doesn't help with the FFI issues though and
> would still have issues. I am always looking for ways to turn
> "compiler-magic" into things that can be expressed to jhc by the user,
> but IO is fairly ingrained at the moment.

Yeah, I think that'd be the best way to do things, and is roughly what
I started working on recently (except for the first draft I figured to
rebuild both jhc and base libraries and then see what I had to
change).

As far as the FFI issue, I can't help but wonder if you could do
something like the following.  If we define

Jhc.IO.wrapIO :: UIO a -> IO a

then we could desugar

foreign import ... foo :: Int -> Bool -> IO Char

to

foreign import ... foo__UIO__ :: Int -> Bool -> UIO Char
foo :: Int -> Bool -> UIO Char
foo a b = wrapIO (foo__UIO__ a b)

Since this is something I could easily imagine doing as a
preprocessing stage, it seems like it ought to be easy enough to do in
the compiler itself.  Then we ought to be able to arbitrarily redefine
IO, as long as we wrote a function to convert from a raw UIO a to IO
a.

Does this sound reasonable to you? Would it be easy for you? I took a
look at the relevant code, and concluded that it wouldn't be easy for
me.

>> I find the idea of writing the Haskell runtime in plain old Haskell,
>> simply by wrapping a less featureful IO very appealing, albeit likely
>> to be less efficient.  But alas, it doesn't look pleasant trying to
>> work this as an option into Jhc.  btw, I've gotten my Haskell IO monad
>> to do exceptions as well as concurrency... although of course it can't
>> handle errors in pure code without some sort of help... but then,
>> that's inevitable.  All implemented without even unsafePerformIO or
>> unsafeInterleaveIO... which are themselves still unimplemented, and
>> probably won't be able to spawn threads until such time as I move the
>> ThreadState into an IORef or something of the sort.  But then, an
>> unsafePerformIO that spawns a thread that outlasts its result is a
>> pretty unholy beast.
>
> Yes, those ideas are definitely appealing. I originally implemented
> exceptions directly in the IO monad, but found them to greatly clutter
> up the generated code for paths that were rarely used. I ended up
> thinking a middle ground would be in order, I didn't want to hardcode
> too much in a static C RTS, but implementing it directly in haskell
> seemed problematic, so I decided to include some appropriate Grin
> primitives and use those. Grin is a purely functional first order
> language, so it is still fairly easy to optimize, but it is close enough
> to what is actually run on the hardware that reasoning about things like
> exceptions and run-time cost is feasable.

Yeah, I don't think that the pure Haskell approach to exceptions (or
concurrency) is necessarily going to win in the long run... it almost
certainly won't.  But having such an implementation still seems like a
good idea.  For one thing, it could be portable across compilers.  And
who knows? It may be that with adequate optimization, you can make
such an implementation behave just as well as a more low-level
implementation.  And, of course, a second implementation may be
helpful in ferreting out bugs in the first.

> As for concurrency, I am leaning more towards a single pthread per
> haskell thread, however, doing things like blackholing would be
> exceedingly tricky in jhc, which raises the question of whether it
> actually is needed. Work may be repeated in theory, but I don't know if
> that will be an issue in practice. unsafePerformIO is more of an issue.
> Perhaps explicit locking can be used for those odd cases... In any case,
> I'd want it to be something that is relatively independent of the rest
> of the compiler, and can be turned on or off on a case-by-case basis.
> like how the garbage collector is fairly decoupled.

That sounds reasonable to me.  I'm pretty well convinced that my pure
haskell approach won't perform very well.  But then, that may not
matter for most workloads, in particular for many things that I do.
And as with a pure-Haskell exception implementation, it seems valuable
to have multiple implementations.  e.g. it might be nice to experiment
with better semantics than those provided by ghc, such as having the
program not exit until all threads have exited.

David


More information about the jhc mailing list