
Lennart Augustsson wrote:
Adrian Hey wrote:
Or put another way, would it be possible to implement the socket API, exactly as it currently is, entirely in Haskell, starting with nothing but hardware? I don't believe it is possible, but perhaps somebody can show me I'm wrong. If I get to implement the IO monad, sure. :)
Perhaps you could give some indication of what you have in mind? AFAICS the problem, if it is a problem, is a semantic problem that afflicts all IO monad implementations. What would be different about your implementation?
IME, the approach you take to these kinds of problems can vary depending what you know or don't know for certain about the system your working with. But you always end up using top level mutable state somewhere along the way. I can only assume folk who insist it's unnecessary (or worse) have never actually tried implementing an IO sub-system from the ground up, starting with nothing but bare hardware. I've written about 50000 lines of USB devices drivers for *BSD (in C). They work from the bare metal and up. They contain no global mutable state (except for variables that define debugging levels, because you need to access these from the in-kernel debugger).
Yes, I was aware of this. But with all due respect, if you're running with an OS already present (not what I would call bare metal :-), your device driver alone is not the entire IO sub-system. I'm not sure what you mean by it having no mutable state. Do you mean no top level mutable state, or do you mean there is no mutable state whatsoever associated with a particular USB port(device..whatever)? I assume you mean the former, and that the mutable device state itself is passed as a parameter to your code, some how. If so I think this is fine and good practice (even my noddy device driver on the wiki does this). But it seems to me when you consider the system as whole, the real problem is the creation, management and aquisition of the device states (state handles) by software that uses particular devices. I'll try to keep this brief, maybe you or someone else can explain the flaw in my logic, if there is one.. Consider a bit of code in the IO monad that needs a device handle to perform some IO action with the corresponding device. How does it acquire this handle? I can think of 3 ways.. 1- It receives it as an argument. No problem, except when I look at the Haskell libraries most of them don't seem to be taking such arguments (main takes none whatsoever). 2- It creates it locally with some kind of newDeviceState constructor. Well there might be some particularly privileged bit of code somewhere in the system that is allowed to do this, but clearly this isn't an option for most code. 3- It gets from some other data structure which is accessible at the top level. IOW a "global variable". This doesn't seem to be an option either in a language that has been designed to prevent the creation of such things. Unless I'm missing something, there seems to be a very serious problem somewhere. If so, it should be fixed IMO, one way or another. Now as JM mentioned, the reason nobody seems to notice this problem is that in reality folk *are* relying on "global variables" somewhere in the system. But they're buried so deeply in unsafePerformIO hacked Haskell libraries, C libraries and OS internals that nobody notices. They're just accepted as being part of "the world". So when people say things like "I have never ever had to use a global variable", I'm inclined to believe them. But I also say that the only reason they can claim to be without "sin" is because somebody else has already sinned on their behalf to provide them with the IO libraries they use. Regards -- Adrian Hey