가변 변수에 대한 John Carmack의 견해
(twitter.com/id_aa_carmack)- John Carmack이 가변 변수(mutable variable) 사용에 대한 개인적 견해를 공유
- 파이썬을 사용하면서 ‘단일 할당(single assignment)’ 원칙을 소홀히 하게 되었다며, 이를 스스로 경계해야 한다고 언급
- 루프의 반복 계산을 제외하고는 변수 재할당이나 갱신을 피해야 함을 강조
- 중간 계산 단계들이 모두 남아 있으면 디버깅 시 도움이 되며, 코드 블록을 이동할 때 의도치 않게 이전 값이 사용되는 문제를 방지할 수 있음
- C/C++에서는 거의 모든 변수를 초기화 시점에 const로 선언하는 것이 좋은 습관이라고 설명
- 마지막으로 “mutable이 키워드였으면 좋겠다”라며, 불변성이 기본값이 되길 바란다고 강조
Hacker News 의견
-
2년간 Clojure를 사용한 후, 불변성이 주는 명료함을 다른 개발자에게 설명하기가 정말 어렵다는 걸 느꼈음
상태 변화를 통해 효과를 일으키는 사고방식에 익숙한 사람들은 직접 경험해보기 전에는 이해하기 힘듦- 변수 변경은 암묵적인 순서 의존성을 만들어냄
예를 들어x = 7; x = x + 3; x = x / 2처럼 작성하면 순서를 바꿔도 오류는 없지만 결과가 달라짐
반면x1,x2처럼 새 변수를 쓰면 잘못된 순서에서 오류가 발생해 문제를 명확히 드러냄
결국 단일 할당(single assignment)은 이런 의존성을 명시적으로 표현하는 방식임 - 나도 Scheme에서 비슷한 경험을 했음
함수형 언어를 써본 적 없는 동료들에게 함수 중심의 사고가 얼마나 테스트하기 쉽고 깔끔한지 설명해도 잘 와닿지 않았음
Python은 함수형 스타일을 읽기 좋게 쓰기 어렵고, JS가 오히려 더 낫다고 느낌
결국 호기심 많은 개발자만이 Clojure 같은 언어를 시도하게 됨 - 불변 데이터와 순수 함수를 기본으로 하면, 함수는 완전히 블랙박스처럼 다룰 수 있음
함수는 외부 상태를 몰라도 되고, 외부도 함수 내부를 몰라도 됨
프로그램 전체 상태를 몰라도 특정 함수만 독립적으로 테스트하거나 디버깅할 수 있음 - 불변성과 가변성을 단순히 대립시키는 건 복잡성을 회피하는 것임
Haskell 커뮤니티가 결국은 타입 시스템 안에서 가변성을 재발명하려는 걸 보면 흥미로움
핵심은 부작용을 최소한의 비용으로 통제하는 것임 - Clojure는 내가 배운 언어 중 가장 영향력 있는 언어였음
Haskell은 타입 시스템 때문에 진입 장벽이 높았고, F#은 너무 타협적이라 C# 문법으로 코딩하게 됨
Clojure의 homoiconicity와 강력한 데이터 구조 덕분에 ‘값으로 일한다’는 개념이 처음으로 명확히 와닿았음
직업적으로 쓰진 않겠지만, 함수형 언어나 Lisp 경험이 없는 사람에게는 꼭 추천하고 싶음
- 변수 변경은 암묵적인 순서 의존성을 만들어냄
-
변수는 기본적으로 불변이고 모든 것이 표현식(expression) 이었으면 좋겠음
하지만 현실은 Clojure 개발자로서 Python의 침공에 시달리는 중임- 나도 Python 개발자지만 개인 프로젝트에서만 Clojure를 써봄
이제는 TypeScript의 침공에 시달리고 있어서 공감됨 -
Rust를 배우고 나서, 언어가 순수 함수형이 아니어도 모든 것이 표현식일 수 있다는 걸 깨달았음
이 방식은 변경 범위를 제한하는 데 정말 유용함 - Clojure는 언제나 Python보다 빠름, 그건 위안이 됨
- 당신은 단순히 Clojure를 사용하는 사람이지, “Clojure 프로그래머”로 자신을 규정할 필요는 없음
언어 간의 부족 전쟁에 휘말릴 필요 없음
생산성 향상 시대에는 경계가 무의미함
Don’t Call Yourself a Programmer 글을 추천함
- 나도 Python 개발자지만 개인 프로젝트에서만 Clojure를 써봄
-
변수 재할당은 최소화하려 하지만, 종종 변수 섀도잉을 사용함
result = result.process()같은 패턴이 간결해서 선호함- 추상적인 예시라 그렇겠지만, 대부분의 경우엔 각 단계마다 명확한 이름을 붙일 수 있음
- 이런 패턴은 보안 버그를 유발할 수 있음
예를 들어process()가 검증 함수라면, 어떤 시점에 처리된 값인지 불분명해질 수 있음
따라서 이름으로 상태를 명확히 구분하는 게 좋음 - 함수형 스타일에서는 이런 중간 변수 없이 함수 체이닝으로 해결 가능함
예:result = x |> foo |> bar |> baz또는(-> x foo bar baz) - “
result.process()라니, 도대체 어떤 result고 어떤 process인가?”
나중에 코드를 읽는 사람은 혼란스러움 - 이미 결과(result)인데 다시 처리(process)한다는 건 논리적으로 어색함
-
“변수(variable)”라는 용어 자체가 늘 마음에 걸림
불변인데 왜 variable이라 부르는가?- 변수는 실행 중에는 변하지 않지만, 호출마다 값이 달라질 수 있음
Rust에서는mut로 명시해야만 변경 가능함
반면 C에서는 상수를 만들려면 전처리기를 써야 해서 혼란스러움 - 함수의 인자
x는 호출마다 다른 값을 받으므로, 그 자체로 변하는 값임
재할당이 없어도 변수라 부를 수 있음 - 수학에서도 변수는 특정 객체가 아닌 임의의 값을 가리키는 기호임
프로그래밍이 이 개념을 그대로 가져온 것임 - 결국 변수는 실행마다 값이 달라질 수 있기 때문에 variable임
상수(constant)는 모든 실행에서 동일한 값을 가짐
Variable (mathematics) 참고 - ‘변수’가 시간에 따라 변한다기보다 맥락에 따라 달라진다는 의미로 쓰이는 것임
- 변수는 실행 중에는 변하지 않지만, 호출마다 값이 달라질 수 있음
-
IDE가 변수의 변경 여부를 시각적으로 표시해주면 좋겠음
예를 들어, 변경된 변수는 살짝 표시만 해주는 식으로- IntelliJ에서는 재할당된 변수에 밑줄이 생기고, 마우스를 올리면 ‘Reassigned local variable’이라는 힌트를 보여줌
가능한 한final을 많이 쓰면 코드가 읽기 쉽고 유지보수하기 쉬움 - 하지만 자동 추론보다는 명시적 opt-in이 낫다고 생각함
IDE가 경고를 주고, 정말 필요한 경우에만 변경을 허용하는 게 좋음
Rich Hickey의 set vs list 이야기처럼, 의미를 명확히 표현하는 구조를 선택해야 함 - Swift는 컴파일러가 변수의 변경 여부를 감지해, 불필요한 변경이면 상수로 바꾸라고 제안함
- JetBrains IDE에는 변수의 읽기/쓰기 위치를 찾는 기능이 이미 있음
- 이런 기능을 하는 린터(linter) 를 만든다면 이름은 “mutalator”가 어울릴 듯함
- IntelliJ에서는 재할당된 변수에 밑줄이 생기고, 마우스를 올리면 ‘Reassigned local variable’이라는 힌트를 보여줌
-
예전에 스레드 안전성을 위해 불변성을 엄격히 적용한 프로젝트를 했음
덕분에 코드가 읽기 쉬워지고, 무엇이 바뀔 수 있는지 추적하기 쉬워졌음
이후로 불변성의 열렬한 팬이 됨- 그렇다면 Rust를 꼭 써보라고 권하고 싶음
-
대규모 Haskell 코드베이스에서 일하다가 다시 C로 돌아오니, 불변성이 기본이었으면 좋겠다는 생각이 듦
const로는 부족함- C에서는 사실 직접적인 변경이 아니라 값 재할당만 가능함
변경하려면 포인터를 써야 하고, C++은 함수 호출만으로도 인자를 바꿀 수 있어서 불투명함 - Rust의 기본값이 충분히 안전한 불변성을 제공하는지 궁금함
- C에서는 사실 직접적인 변경이 아니라 값 재할당만 가능함
-
“거의 모든 변수를 const로 선언하는 게 좋다”는 말에 공감함
Rust가 언급될 만함- 관련해서 John Carmack의 트윗을 참고할 만함
- 그리고 Zig도 같은 철학을 따름
-
불변이 기본이고, 특정 블록 안에서만 mutable을 허용하는 문법을 상상해봄
예를 들어 Python의with블록처럼with mutable(x, items): x = 3 items.append(4)블록을 벗어나면 다시 불변으로 돌아가는 식임
- 이건 사실상 mutable borrow 개념임
Rust의 borrow checker를 보면 이 개념이 얼마나 복잡한지 알 수 있음 - borrow 체크가 없다면, 블록 밖에서도 참조가 남아 변경될 수 있음
- 이건 사실상 mutable borrow 개념임
-
“State is the enemy”라는 말이 있음
상태가 늘어날수록 테스트해야 할 조건이 기하급수적으로 늘어남
불변성은 이런 상태 폭발을 막는 방법임- 그래서 함수형 프로그래머들은 Church와 state의 분리를 믿음
Separation of Church and State
- 그래서 함수형 프로그래머들은 Church와 state의 분리를 믿음