Lennart Augustsson writes:
> 
> Fergus Henderson wrote:
> > Sigbjorn Finne, you wrote:
> > > 
> > > I don't honestly see what having these handles as constant *gain* you,
> > > so why then have them as such, if not having them constant gives you
> > > extra expressiveness?
> > 
> > But, unless I'm missing something, making them non-constant doesn't
> > give you any extra expressiveness.
> > 
> > Remember that the fact they are constant does not imply that the
> > file they are connected to is constant.  Even if they are effectively
> > thread-local, i.e if the file that stdin and stdout are connected to
> > depends on which thread you're in, stdin and stdout themselves can
> > still be constants, can't they?
> Exactly!
> 

OK, so stdin is not a global constant anymore, but is stashed away in
TLS somewhere. I don't like this at all, for the following reasons:

- one of the touted benefits of using continuations is the ease
  by which you can implement a multi-threaded system, Schemers and ML
  folks have been doing this for years, as you no doubt know better
  than me. Threads in such an implementation have no TLS, that's the beauty
  of them, so your solution would preclude the use of continuations
  here, right? (same goes for Concurrent Haskell, it does not use
  continuations as such, but only record the STG state of a thread in
  a special heap object). 
  
- If you do require multi-threaded Haskell implementations to have
  TLS, your solution would be inefficient, what if you were to
  communicate a value like the following between two threads

     (\x y -> hPutStr (if x then stdout else y) "Ay,ay,ay!")

  do you then have to walk over the thunk you're transmitting to
  to hunt for the uses of the thread-local handles, and somehow
  relativise them to the thread you're communicating with? 

But, the standard handles can be made thread-local, just provide
access to them through IO! A straightforward implementation
of IO in a multi-threaded setting might be

 type Triple a = (a,a,a)
 newtype IO a = IO (Triple Handle -> _RealWorld -> (_RealWorld, a))

Creating new processes (using Concurrent Haskell's forkIO, say) can
then easily be augmented to functionally change the interpretation of
the standard handles for a process. This solution is simple, modular
and does not require TLS or other RTS Magic.

> I regard stdin, stdout, and stderr as names for abstract versions of
> 0, 1, and 2 (does my background in C programming on Unix show? :-).
> These are just handles, the can be reconnected to anything.  If you
> argue that you should be able to change the meaning of stdin I could
> argue "This piece of code I've got uses the constant 5, this is not
> what I want, I need to change the value of 5 locally when that code
> runs.  Give me the machinery to do that!"
> 

OK if you *really* want that, it's easy enough to do:

   newtype IntVal = IntVal ((Int -> Int) -> Int)
  
   lift :: Int -> IntVal
   lift v = IntVal (\ f -> v f)
  
   run :: IntVal -> (Int->Int) -> Int
   run (IntVal v) f = v f

   valPlus :: IntVal -> IntVal -> IntVal
   valPlus (Val v1) (Val v2) = Val (\ f -> v1 f + v2 f)

   instance Num IntVal where
     (+) = valPlus
     fromInt n = lift n     -- not standard haskell, but you get the idea.
     ...

   main = print (run (2+2) (+1))

   -- prints 6 :-) :-)

I'm only arguing that make the standard handles be connected to IO,
which is the only context they can *ever* be used. Doing so, adds
valuable expressiveness over stdio, which I consider a Good.
But then again, Dennis Ritchie is not my hero :-)

--Sigbjorn



Reply via email to