4 ms·
While (map) is pure, (map print) is not. Thus map is a higher-order function that can create both pure and impure functions depending upon the purity of its arg
by tmoertel 13y ago
While (map) is pure, (map print) is not. Thus map is a higher-order function that can create both pure and impure functions depending upon the purity of its arguments. How then would your syntactic scheme allow for higher-order functions?
- mercurial 13y agomap print doesn't have side effects. mapM_ print, certainly.
- tmoertel 13y ago(mapM_ print) produces a pure function that produces a single IO action, and (map print) produces a pure function that produces a list of IO actions. Neither has any effect unless called in a context that sequences the resultant actions into the main IO action that the runtime interprets (or a thread's action).
- mercurial 13y agoI meant mapM_ in an IO action actually executed. On the other hand, getting a list of IO actions, even in the IO monad, doesn't amount to much.
- tmoertel 13y agoAnd my point is that the action that mapM_ ultimately produces is not actually executed unless you sequence it into the main action (or a thread's action). Since mapM_'s eventual action is a singleton, you can do this sequencing with any combinator taking a singleton action, for example (>>) or (>>=), but it must be sequenced nonetheless, the same as for the list of actions that (map print) produces, if you want those actions to be executed.
- mercurial 13y agoFair enough.
- chongli 13y agoActually, in Haskell (map print) is pure; it just doesn't do what you expect it to: >>> map print ["hello","world","!"] <interactive>:2:1: No instance for (Show (IO ())) arising from a use of `print' Possible fix: add an instance declaration for (Show (IO ())) In a stmt of an interactive GHCi command: print it What's going on here? Let's look at the type of (map print) to find out: >>> :t (map print) (map print) :: Show a => [a] -> [IO ()] (map print) is a function which takes a list of values of type a -- such that a can be shown as a String; hence the Show a => constraint -- and returns a list of values of type IO () (pronounced IO unit). These are monadic values representing computations that perform the actual IO. Hence, (map print) is a pure function which carries no side effects. So, what the heck do we do with this strange list of IO ()s? Well, one answer is to pass them to sequence_: >>> sequence_ (map print ["hello","world","!"]) "hello" "world" "!" Ahhh, so now we get to the impure function: sequence_! Actually, sequence_ is a pure function as well. Its type is: >>> :t sequence_ sequence_ :: Monad m => [m a] -> m () sequence_ merely takes a list of monadic values and combines them into a single monadic value, discarding any of the elements' return values and returning () instead. So if everything is a pure function, how do we actually perform the side effects? The simplest way to think of it is that our whole program is a bunch of pure functions which construct a single value representing all of the side effects that will take place over the lifetime of the program. This single value is called main: main :: IO () main = sequence_ (map print ["hello","world","!"]) With this idea in mind, we can think of Haskell's runtime as taking this single value main and performing the side effects specified throughout our program.
- tmoertel 13y agoActually, every function in Haskell is pure. It's just that some of those pure functions produce values of type (IO a) representing IO actions and, if you sequence those actions into the main action (or a subthread's action), those actions will be performed by the runtime. So when people say that some functions in Haskell are "impure" they mean that they produce IO actions that, if sequenced, will depend upon or cause IO effects. Thus, both map print ["hello","world","!"] :: [IO ()] and mapM_ print ["hello","world","!"] :: IO () are equally pure or impure: They both produce actions that have side effects if sequenced. It's just that the first must be sequenced differently than the second since it produces a list of actions and not a singleton action. Since singletons can be sequenced with (>>) and (>>=) you can insert them directly into do notation, which makes many people believe that they are somehow different in terms of purity. (But they are not.)