Hacker News new | ask | show | jobs
by pwnstigator 6015 days ago
Functions should only have side effects if and only if that's what the function is designed to do: e.g. perform IO, write to disk, et cetera. Then, "side effect" is a misnomer, because the alteration to state is intended.

The hatred for "side effects" comes largely from a prehistoric tendency of programmers to write highly optimized in-place operations that destroyed the original data. An example would be a matrix multiplication that destroys one of the original matrices, or Common Lisp's NCONC (a faster APPEND that destroys some of the lists passed as arguments).

The general guiding principle of good code is that visible state changes occur only when requested. (For optimization, private state changes can be used, such as caching/memoization, but these are behind a layer of abstraction and don't violate the referential transparency of the API.)

2 comments

>Functions should only have side effects if and only if that's what the function is designed to do: e.g. perform IO, write to disk, et cetera. Then, "side effect" is a misnomer, because the alteration to state is intended.

In medicine, they have a saying, "There are no side effects, only effects." I first heard that from a psychiatrist, discussing how he initially selects antidepressants for his patients based on side effects, such as weight loss or gain. Viagra was originally a heart medication.

What the discussion really comes down to is, what is the intent of the programmer? If the reason for a function call is a return value, but there's also a state change, then a problem will almost always arise because you want one but not the other. That's why change of state and global variables are usually maligned, because it couples disparate behavior, sometimes in unintentional or obfuscated fashion.

Further reading: http://en.wikipedia.org/wiki/Command-query_separation

Eiffel is apparently designed such that anything with a return value causes no change of state, and vice-versa. It's an interesting idea because it means that a change state, like you said, is never a side-effect, but always the intention of the caller.

Like pretty much almost everything, anywhere, ever, it's probably better as a general programming practice rather than a language-enforced restriction. And that's basically what the author was saying about purity--it's better as a guideline than a commandment.

I generally agree with the principle of command-query separation. One point where I would differ is that, when the command's effects aren't entirely known to the caller, it's often useful to return this information.

For example, a function that creates a user account with a unique numerical user ID, that isn't known until the account is created, can return the user's ID, e.g.

  (create-user-account "Bob") => 9001

  (defn create-acct-and-immediately-do-something [username]
     (let [user-id (create-user-account username)]
        (do-something-with-acct user-id)))
Sounds a lot like encapsulation to me, only without Objects to delimit the boundaries.