함수가 변수의 값을 변경할 때 발생할 수 있다.
expression에 포함된 함수가 expression의 다른 피연산자의 값을 변경할 때 발생할 수 있다.
#include <stdio.h>
int changeInt(int * p){
*p = (*p)*(*p);
return *p;
}
int main() {
int a = 7;
a = changeInt(&a) + a;
printf("%d", a); // 98
return 0;
}
위의 코드를 실행해보자.
위의 값의 결과로 출력되는 Integer 변수 a의 값은 7 + 7 x 7인 56이 되어야 하는 걸까?
아니면 function changeInt의 결과로 a의 값이 변경되어 49 + 7 x 7인 98이 되어야 하는 걸까?
🍶 위의 코드를 실행해보면, 56의 값을 얻을 수 있다.
그렇다면, 위의 코드에서 a = changeInt(&a) + a를 a = a + changeInt(&a)로 바꾼다면, 다른 값이 나올까?
그렇다.
🍾 a = a + changeInt(&a)로 expression을 바꾼다면, 출력되는 값은 56이 된다.
둘 중에 어떤 값을 원하는지는 코드를 작성하는 사람에 따라 다를 수 있다.
하지만, 순서를 잘못 작성할 경우 원치 않는 결과를 얻을 수 있기에, 함수를 사용해 변수의 값을 바꿀 때에는 이에 항상 유의해야 한다.
🥧 전역 변수를 사용할 때에도 이런 일이 발생할 수 있다.
이렇듯, functional side effects를 해결하기 위해서 피연산자의 평가 순서를 고정하는 방법이 있다.
🥢 예를 들면, Java는 피연산자의 평가가 왼쪽에서 오른쪽 방향으로 이루어진다. C언어도 그렇다.
이는 일부 컴파일러의 최적화를 제한하는 단점이 있기도 하다.
또 다른 해결방법으로 아예 functional side effects을 허용하지 않는 언어가 있을 수 있다.
함수에 양방향 매개변수가 없고, local이 아닌 references가 없으면 된다.
하지만, 이 방법은 non-local 매개변수를 사용할 수 없고, 양방향 매개변수도 사용할 수 없는 융통성 없는 언어가 되버린다.
예를 들어, parameter로 Integer a를 받아 Integer b를 반환하는 함수 offerInt가 있다고 하자.
프로그램의 어디에서 호출하든 Functional side effects가 발생하지 않고 offerInt(a)가 항상 같은 값인 b를 반환한다면, 이 프로그램은 referential transparency하다.
🍮 이러한 속성을 갖는 프로그램의 의미는 훨씬 이해하기 쉽다.