Hello, What if main had a function prototype like: main :: String -> String And you passed stdin to main as string, and you print the output of main to stdout. As long as you run the program with the same stdin, when main calls 'ask_user_name stdin', you will always get the same answer, right ? And, you have not violated referencial transparency -- for the same input, you get the same output, and there are no side-effects. Back in the day, this is approximately how IO was done in haskell. See the function 'interact' for more information about that method. That method obviously has limitations, such as only being able to read from stdin and write to stdout -- most real programs want to read from lots of files, or talk to a network, interact with a mouse cursor, etc. But the basic idea can be extended -- imagine if main had a function prototype more like, main :: RealWorld -> RealWorld Where RealWorld is not just stdin, but everything that your program is going to interact with -- files on the disk, network sockets, etc. Then you could do things like, main rw = let (username, rw') = get_user_name rw in let rw'' = putStrLn rw' username in rw'' As long as you pass in the same rw everytime, you should get the same results. Of course, everytime something like get_user_name or putStrLn interacts with the rw, it changes it (for example, the file pointer moves). Since we can't have 'side-effects', putStrLn or get_user_name must return a new version of rw that reflects the changes they have made. So, we are still maintaining referecial transparency (for the same input you get the same output), and there are no 'side-effects' -- all the effects of the function are explicitly returned as a modified version of rw. Of course, writing the above code is really tricky in practice, because you can only use each version of rw once -- otherwise you would be using an old state of the world that is no longer accurate. It is very easy to accidently use the same version of rw more than once. In concurrent clean, they solve this problem by having the compiler do uniqueness type checking. The compiler knows that each version of rw can only be used once and it will generate a compile time error if you accidently use it twice. In haskell, we solve the problem by using monads to ensure that each copy of rw is only used once. As a matter of fact, monads free the user from having to deal directly with rw at all -- which is very nice. The important thing to realize is that monads (aka, do ... <- work), are not some hack where we suddenly allow side-effects. Monads are useful for many things -- one of which is making sure state variables are used in a sane manner. The IO monad is designed specifically handling the RealWorld state so you can do useful IO. If you read the paper reference in another message (Tackling the awkward squad), I believe he covers this in more detail (and more clarity). In any case, as long as the same RealWorld is passed into the program everytime, you get the same output everytime. The trick is getting RealWorld to be exactly the same each time. Conceptually, RealWorld needs to contain the entire state of the world that could affect your program. So, if you hard drive is destined to crash half way through the program -- that needs to be encoded into RealWorld. Pragmatically, RealWorld can only encode a limited amount of information, like file handles, etc. Because RealWorld is missing some information, you can get different results from one run to the next, even if RealWorld is identical. For example, if you type in a different name to ask_user_name, then you will get a different result, even if the inital RealWorld was the same. Techinally, the name that you are going to type in, should be stored in RealWorld when the program starts, but no one has figured out how to see that far into the future yet... I hope this makes some sense (and I hope what I have said is reasonably correct...) Jeremy Shaw. On May 02, 2005 09:38 PM, Daniel Carrera <dcarrera@digitaldistribution.com> wrote:
Hi all,
Again, I'm the new guy slowly learning this "fuctional programming" thing. :-)
I've been reading, and I'm really liking the elgance of Haskell, and FP as a whole. But I wonder about the range of applicability. You see, one of the things about FP is that there are no side-effects and the same function on the same parameters always returns the same value. But any program that interacts with the real world cannot meet those properties.
The function ask_user_name() will not return the same value each time. If write a chess program, the function get_user_move() will be different each time. And every time you update the screen, you're having a side-effect.
So, I figure that to do these tasks you heed that "do ... <-" work around. But that kills the whole point of using FP in the first place, right?
So, I'm tempted to conclude that FP is only applicable to situations where user interaction is a small part of the program. For example, for simulations.
Now, I'm sure I'm not the first person to have this train of thought. And I'm sure there is a good answer why I'm wrong. :-) I'm eager to hear what that might be.
Cheers, Daniel. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe