6 ms·
isn't IO inherently not functional? it's a side effect
by themagicalcake 6y ago
isn't IO inherently not functional? it's a side effect
- lgas 6y agoIn Haskell your code doesn't "do IO", you write (and compose) IO actions which describe what IO will be done at runtime. Only at runtime do the side effects occur. The code itself is still pure and referentially transparent.
- kryptiskt 6y agoNo, not if you include the state of the world in the type signature. If you make every primitive IO function take a RealWorld value and return one, you can thread the execution correctly using the last one as an argument to the next. But then the question is what happens if you reuse a RealWorld that you already called a function with. That leads you to types with values that can only be used once, uniqueness types (https://en.wikipedia.org/wiki/Uniqueness_type https://en.wikipedia.org/wiki/Uniqueness_type). Which is the solution for IO that Clean, the other lazy functional language, uses.
- saithound 6y agoTo add to lgas' point: The distinction between functions with side effects and descriptions of what IO will be done at run-time has many practical consequences. For example, if you execute the Haskell code greet name = do putStrLn ("Hello, " ++ name) repeat3 action = do putStrLn "Doing it thrice." action action action putStrLn "Done it thrice." main = repeat3 (greet "themagicalcake") you get the following output: Doing it thrice. Hello, themagicalcake Hello, themagicalcake Hello, themagicalcake Done it thrice. But if you execute the similar-looking Python script def greet(name): print ('Hello %s' % name) def repeat3(action): print ('Doing it thrice.') action() action() action() print ('Done it thrice.') if __name__ == "__main__": repeat3(greet('themagicalcake')) you get Hello themagicalcake Doing it thrice. Traceback (most recent call last): File "test.py", line 10, in <module> repeat3(greet('themagicalcake')) File "test.py", line 5, in repeat3 action() TypeError: 'NoneType' object is not callable instead. The Haskell value `greet "themagicalcake"` has type `IO ()`: it is a value that describes IO to be done at runtime. In contrast, the Python value returned by the call `greet('themagicalcake')` has type `NoneType`, and this value does not describe anything. Calling `greet` itself had the side-effect of printing "Hello themagicalcake".
- kqr 6y ago...and the ability to treat I/O computations (these "descriptions" of I/O to perform) as plain values in the code is the reasons Haskell is sometimes affectionately known as "the best imperative language". It's not that you couldn't do the same thing in Python, it's that you get more fine and precise control over what happens by default in Haskell.
- lalos 6y agowould this work on python? repeat3(lambda: greet('themagicalcake'))
- saithound 6y agoIt would work in the sense that it would not throw an exception, but it wouldn't work the same way in every situation. Haskell's `greet "themagicalcake"` does not correspond to Python's `greet('themagicalcake')`, nor does it correspond to Python's `lambda: greet('themagicalcake')` in every situation. E.g. you can write main = greet "themagicalcake" in Haskell to get a program that just prints Hello themagicalcake but the Python program if __name__ == "__main__": lambda: repeat3(greet('themagicalcake')) prints nothing.
- somewhereoutth 6y agoDo we even need side effects? Most programs seem to be about transforming some input to an output and/or storing/retrieving state in a transactional manner. Perhaps I/O and transactional storage could be kept outside a language and instead be managed by its containing environment? For example, console I/O: The input from the console could be transformed (possibly with some state) and sent back out (possibly triggering an associated state update) as a fairly natural pure function call. It would have the type state -> input -> (state', output), and the 'runtime' would be responsible for gluing this all together.
- AnimalMuppet 6y agoSuppose it's not the console. Suppose it's a disk file. You can write to it. You can read back in what you wrote out. That means it's a giant global state. If I understand correctly, writing to it cannot be pure.