[Concurrency] 동시성과 함수의 색상(color)
함수의 색상은 동시성 프로그램에서 꽤 떠들썩한 주제 중 하나다.
이른바 동시성이 함수 단위로 "전염"되고 강제되는 것에 대한 부정적 피드백이다.
특히 Go 개발자들이 많이 주장하더라.
함수의 색상과 전염성
색상 문제는 async/await 구조를 지원하는 언어들을 대상으로 지적할 수 있는 문제다.
대표적인 언어로는 Node.js, Rust, C#, Dart, Python 등이 있다.
위 언어들은 현실적인 한계로 인해 다음과 같은 공통적인 제한을 갖고 있다.
"async 함수는 async 함수에서만 정상적으로 호출될 수 있다."
그래서 javascript에서는 async 함수를 다른 곳에서 await하려고 하면 오류가 발생한다.

사실 이건 구현 관점에서 보면 타당한 제한이긴 하다.
await이라는 것이 결과 상태를 polling하거나 event tick 기반으로 대기를 하고 값을 받아오는 것인데, 호출스택 중간에 async가 끊겨있으면 그걸 처리할 방도가 없기 때문이다.
아무튼 여기서 async 함수를 "빨간색" 함수라고 부르고, async가 아닌 함수를 "파란색" 함수라고 부른다.
이런 구조 때문에 의도치 않은 전염성이 비효율성을 증대시킨다는 주장이다.
예를 들어, 기존에 async가 필요하지 않아서 non-async로만 작성된 코드가 있다고 해보자.
여기에 하나의 async 함수만 필요해진다면 이 제한 때문에 전체 함수 호출 스택을 async로 재작성하는 강제 오염이 발생할 수 있는 것이다.
이런 번거로움을 방지하는 가장 간단한 방법은, 특별한 이유가 없다면 처음부터 상위 호출 스택을 async 기반으로 잡는 것이다.
그래서 go user들은 non-async와 async 함수가 전혀 구분되지 않는 go의 goroutine이 훨씬 간결하고 편리하다고 주장하곤 한다.
async/await는 나쁜가?
세상 모든 것이 다 그렇지만, 이것도 trade-off다.
async/await은 특유의 구조로 말미암아 "비동기 함수"와 "동기 함수"를 명확하게 구분할 수 있고, 비동기 함수의 대기(await)를 매우 명시적으로 지정할 수 있다.
async/await 구문이 없는 언어에서 유사한 await 패턴을 구현하려면 매우 조잡하고 비직관적인 코드가 나오기가 쉽다. Go가 특히 그런 경향이 심하다.
async/await은 가독성과 직관성 측면에서 매우 강력한 지점이 존재한다.
성능 면에서도 꽤 효율적인 부분이 존재할 수 있다. (Node.js, Python 제외)
언어마다 다르지만, C#과 Rust의 경우에는 async/await 흐름을 컴파일해서 최적화된 형태로 재구성한다. 때문에 스위칭에 따른 런타임 부하가 매우 적다.
기타: 동시성 바깥
함수의 색상 이론은 동시성 구조에만 국한되는 것은 아니다.
함수의 종류가 구분되고 상호간 호출에 제한이 생긴다면 그건 다 "색상"의 차이로 간주한다. 다 정도의 차이가 있을 뿐, 대부분의 언어들에는 함수의 색상이 존재한다.
예를 들어 Rust의 unsafe 함수는 unsafe에서만 실행될 수 있다. 따라서 unsafe는 "빨간색" 함수다.
Java의 경우에는 함수 서명에 throws를 붙일 수 있는데, 이러면 함수 자체에 예외처리에 대한 제한이 생긴다. 따라서 throws가 붙은 함수는 빨간색 함수다.
참조
https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
https://langdev.stackexchange.com/questions/3430/colored-vs-uncolored-functions
https://www.danmailloux.com/blog/colored-functions-are-good-actually