Should go out of my way to make my functions pure and self contained with no side effects?
I will answer in some indirect way but specific to "assembly", hope it helps.
Any function that does not change something outside of arguments and results set is pure. (Rather relaxed definition but good to start.) There is none obstacle against writing pure functions in assembler, provided that definition of pure action is consistent with function calling convention and running environment.
What I mean here: first, imagine a function that
- gets two arguments in R0 and R1
- makes their sum in R2
this is really pure one, if it does the only action - adding of two values.
But, imagine that calling convention requires passing arguments on stack. For x86-32, this will compile to something like
f:
movl 8(%esp), %eax
addl 4(%esp), %eax
ret
this does pure action in sense it doesn't change anything explicitly except returning the value, but there is exception: its calling changes 12 bytes in memory (stack area): 2 arguments and return pointer. This is allowed side effect of function that is pure in another sense.
But[2], if you change it that it adds first argument to a global variable:
f:
movl 4(%esp), %eax
addl %eax, sum_a
addl 8(%esp), %eax
ret
this will not be pure in traditional sense: you have added a side effect.
But[3], if some side effect is explictly declared as not affecting function purity - for example, this adding to sum_a is implemented for debugging and does not change the target functionality of the program - the function can again be considered as pure.
So, "purity" is not an absolute concept. It gets real sense only when declared what real world effects are discarding its purity and what ones are not. Usually, the following effects are keeping purity:
- Execution time.
- Execution system implementation (hardware) effects, as RAM access, cache filling (and draining of previous cache state).
- Operation system (and other software) effects, as task switching, paging in/out.
- Application debugging and monitoring (while goal results are not changed).
What side effects are allowed is up to you. The main thing you would keep in mind is that
- Some effects are unavoidable by design (as cache change).
- Despite this, you should minimize all other side effects, just because they are easy to forget and so will confuse you while writing. Sometimes it needs hours of debugging to catch a Total Recall for a tiny fact of side effect of a function you wrote a few years ago. And this is generally language-independent: assembler or Java, or even LISP, your functions have important side effects, or they don't.
Bonus Question: What is functional programming?
The way I understood it is that it's all about writing self-contained, pure functions that have no side effects.
In general, it is not. But this is offtopic here (I mean both in this topic and this forum, youʼd better go to SE one, unless usual textbooks, wikipedia and googling is not enough.) I would better describe it as programming when you specify implementation in sense of elementary actions as functions and their argument-result relation, without specifying of operation order. But I wonʼt insist on it.