하스켈에 대한 오해

Posted on June 17, 2020

변수(상태)가 없다?

보통 변수가 존재하지 않고, 대입이라는 개념이 없다고 설명하지만, 이들 없이 실용 프로그램을 만들기 어렵습니다. 상태가 계속 바뀌어 가면서, 상태에 따른 처리를 하는 프로그램들이 대부분인데, 상태를 넣어 둘 “변수” 없이 하스켈로 무엇을 할 수 있을까요? 매직은 없습니다.
명령형 언어에서처럼 아무데서나 “편하게 쓰지 못하도록” 해 놨을 뿐이지, 하스켈에도 같은 목적으로 만든 라이브러리들이 존재 합니다.(물론, 이런 “불편함” 덕에 좀 더 함수형으로 생각할 수 있게 합니다.) 바로 IORef, MVar, STM 의 존재입니다. 이들은 거의 전역 변수처럼 쓰입니다. 언어 자체에 빌트인 되어 있는 기능들은 아니지만, 실용 프로그램을 만들려면 반드시 이들의 도움을 받아야 합니다. 정리하면 변수는 없지만, 변수와 같은 일을 하는 라이브러리가 있어, 실용 프로그램을 만들 때 “변수(상태)의 개념”을 아예 빼고 로직을 세우는 건 아닙니다.

언어 설계는 상태가 없고stateless, 변수는 불변immutable이지만, 하스켈로 만든 프로그램에도 “상태 개념”은 존재합니다.

모나드는 비순수 작업을 순수하게 처리한다?

(동일한 입력에 다른 값을 돌려준다거나, 매개 변수에 없는 정보를 접근한다거나 바꿔놓는다거나 하면 Side Effect가 있다고 말합니다. Side Effect를 부작용, 부수효과라고 번역하곤 하는데, 실제 뜻과는 좀 거리가 있어 보이는 번역입니다. 여기서는 Side Effect를 따로 떼어내서 번역하지 않고, Side Effect를 가진 작업을 비순수 작업이라고 부르겠습니다.)

모나드는 비순수 작업을 순수한 모양으로 다룰 수 있게 해 준다고 하는데, 쉽게 얘기하면 비순수 작업과 같은 결과를 가져오는 그냥 좀 복잡한 순수 작업입니다. 무슨말이냐 하면,

f, g, h… 함수 모두가 env 값이 필요하다면, 함수 바깥에 전역 변수같은 걸 둘 수 있는 비함수형에서는 함수들이 공유하는 바깥 스코프에 env를 정의하고, 각 함수에서 접근합니다. 하지만 순수 함수는 매개 변수(람다 컨텍스트 포함)를 통해서만 외부의 정보를 가져올 수 있습니다. 이럴 경우 해결책은? f, g, h … 모든 함수에 env 매개 변수를 만들고, 함수를 부를 때마다 env 를 넘겨주면 됩니다. 좀 귀찮을 뿐이지만 목표는 달성할 수 있습니다. 정말 단순한 생각이라, 하스켈은 이 것말고 뭔가 특별한 개념으로 처리하는 게 아닐까 하고 넘겨 짚습니다. 아닙니다. 모든 함수에 env를 넘기는 단순한 방법으로 해결합니다. 단, 프로그래머가 신경쓰지 않도록 “가려”줍니다. 1 실용 프로그램에서 필요한 수많은 비순수 작업들을, 지루한 방법으로 순수 형태로 해결하는데 이를 모나드 패턴을 써서 가립니다. 대부분의 비순수 작업들은 성가신 작업들을 우직하게 밀고 나가면 순수 작업들로도 같은 결과를 가져 올 수 있습니다.

예를 들어 보면, random 함수를 어떻게 구현할까요? 입력이 같으면 출력이 항상 같아야 하는데, 어떻게 해결할까요? 특별한 방법이 없습니다. 함수를 부를 때마다, 매번 다른 입력값을 주면 됩니다. 진짜로 이렇게 구현합니다. 하지만 모나드 패턴을 이용해 프로그래머는 비순수 언어에서 쓰는 것과 비슷한 모양으로 random 함수를 쓸 수 있습니다.2

전역 변수가 없으니 상태를 기억시킬 방법이 없습니다. 어떻게 해결할까요? 상태를 바꾸는 함수가 작업이 끝나면, 변화된 상태를 결과로 내 뱉고, 그 다음 함수가 상태를 받아 작업을 하고, 또 상태 결과를 뱉고… 이렇게 상태와 관련있는 모든 함수에 상태를 위한 매개 변수를 두어 해결하면 됩니다. 진짜로 이렇게 해결합니다. 하지만, 상태를 넘기고 받는 작업을 눈에 띄지 않게 해서(안 보이는 함수에게) 맡겨서 불편하지 않게 해 줍니다.3

하스켈에서의 모나드는 비순수 작업을 다른 형태의 순수 작업으로 구현한 걸 보기 좋게, 쓰기 좋게 만들어 주는 코드 패턴입니다.

2022.5.2 추가
너무 얕게 아는 상태에서 오만하게 쓴 제목입니다. 아직도 맞는 내용인 것 같긴 한데요…


  1. Reader 모나드↩︎

  2. IO 모나드↩︎

  3. Status 모나드↩︎

Github 계정이 없는 분은 메일로 보내주세요. lionhairdino at gmail.com