Scheme: Procedures vs. Data

I'm going through Structure and Interpretation of Computer Programs right now. One thing Abelson really enjoys is blurring the distinction between procedures and data. Coming from other languages with higher-order functions, I'm comfortable with the idea. For instance, I understand map, filter, reduce, etc. However, he takes the idea much further. He's just as likely to put a bunch of procedures into an abstract data structure as he is to put data into an abstract data structure. Since procedures can carry with them a bit of data via closure, it all works out. Even he calls such procedures "objects".

He loves to mix up data and procedures, because he says it "doesn't really matter." Thinking about that, that's somewhat true and somewhat untrue. Clearly, procedures can be passed around as data, and that's fine. However, there is a difference.

If a procedure takes a value and returns a value, you can think of that procedure as data with blanks that haven't been filled in yet. That's fine. In that case, the distinction between procedures and data isn't that important.

However, if a procedure has side effects like IO, that's very different. For instance, data doesn't ever do stuff like deleting your hard disk. In fact, data isn't even apply-able, so you can't even call it in the first place like you can a procedure. Procedures are only the same as data as long as you never actually call them. As soon as you call them, they have the option of being very different.

Since I'm sure all the Haskell authors have studied Lisp, I can see why they make such a big deal about the io monad. A function that doesn't use the io monad can be treated as inert data. A function that does use the io monad is very different. Sure, you can still mix, match, combine, etc. such a function, but as soon as you apply it, it can change the state of the world. I think that's a real difference.