Actually, functional programming is very straight forward on a low level.
When you define a function, foo, the machine code for that function is at a specific memory location. When the source code says foo(*args), the machine pushes local variables, its current location in the code, and the args to the stack.
It then jumps to the memory location stored in foo
Once there, the code pops the arguments, does stuff, pushes the return value, and jumps back to where it was called from.
Once there, the return values and local variables get pop'ed, and execution continues.
Different compilers might use different conventions, but for our purposes this is a good enough model.
The way a normal function call would look in assembly might be:
mov %eax <returnPoint> ;at assembly time, <returnPoint> is replaced with the memory address of the operation labled returnPoint:
push %eax ;save the return point to the stack
push %ebx ;push local variables and the arguments
...
jmp <foo> ;jump to the memory location labled foo
returnPoint: pop ebx ;pop local variables and return values,
pop ecx
...
The thing to note in this example is that when you run the assembler, <foo> gets replaced with the memory location of the function code. If we want to a functional type thing, we need the address we jump to be able to change at runtime, so instead of having the assembler hard code the memory location to jump to, we need to get that information from a variable. This would look like
...
jmp [foo]
...
This time, foo is not pointing to a function, but rather to the memory location of a function pointer. This means that if we want to make it so when we call foo(), we run the bar() function, we would simply need to do:
mov foo <bar>
foo is the memory location of the function pointer, and <bar> is the memory location of the function itself, so now when we dereference foo in the jmp, we will execute bar().
With regard to the ARM architecture, I've done a little bit of work in it, and it isn't more complex than x86.
The way a normal function call would look in assembly might be:
mov %eax <returnPoint> ;at assembly time, <returnPoint> is replaced with the memory address of the operation labled returnPoint:
push %eax ;save the return point to the stack
push %ebx ;push local variables and the arguments
...
jmp <foo> ;jump to the memory location labled foo
returnPoint: pop ebx ;pop local variables and return values,
pop ecx
...
The thing to note in this example is that when you run the assembler, <foo> gets replaced with the memory location of the function code. If we want to a functional type thing, we need the address we jump to be able to change at runtime, so instead of having the assembler hard code the memory location to jump to, we need to get that information from a variable. This would look like
...
jmp [foo]
...
This time, foo is not pointing to a function, but rather to the memory location of a function pointer. This means that if we want to make it so when we call foo(), we run the bar() function, we would simply need to do:
mov foo <bar>
foo is the memory location of the function pointer, and <bar> is the memory location of the function itself, so now when we dereference foo in the jmp, we will execute bar().
With regard to the ARM architecture, I've done a little bit of work in it, and it isn't more complex than x86.