async 라이브러리로 JavaScript의 콜백헬을 조금이나마 피할 수 있는 모양이다.
뒤늦게 async를 살펴보았다. API들이 잘 이해가 안되었는데, 알고보니 비교적 단순한 원리로 만들어진 것들이었다. sync버전의 대응함수와 비교해보면, 함수 그자체와 iterator 함수들을 모두 async로 바꾼것에 불과하다.
each :: Array[A] -> (A->Unit) -> Unit
async.each :: Array[A] -> (A->(Unit->Unit)->Unit) ->(Unit->Unit)->Unit
map :: Array[A] -> (A->B) -> Array[B]
async.map :: Array[A] -> (A->(B->Unit)->Unit) -> (Array[B]->Unit)->Unit
filter :: Array[A] -> (A->Bool) -> Array[A]
async.filter :: Array[A] -> (A->(Bool->Unit)->Unit) -> (Array[A]->Unit)->Unit
reduce :: Array[A] -> B -> (B->A->B) -> B
async.reduce :: Array[A] -> B -> (B->A-(B->Unit)->Unit) -> (B->Unit)->Unit
sortBy :: Array[A] -> (A->B) -> Array[A]
async.sortBy :: Array[A] -> (A->(B->Unit)->Unit) -> (Array[A]->Unit)->Unit
mapcat :: Array[A] -> (A->Array[B]) -> Array[B]
async.mapcat :: Array[A] -> (A->(Array[B]->Unit)->Unit) -> (Array[B]->Unit)->Unit
처음 헷갈렸던 이유가 Continuation 자체에 대한 map을 생각한 탓이었다.
async API중에서 이들 Array유사 함수들 말고,
waterfall
을 많이 쓰는 모양이다.
이 함수는 첫 인자인 Array에 노드 스타일 비동기 함수들을 넣어서 이들을 콜백으로 꿰어 실행해준다.
배열의 인자를 두개만 가정하면..
async.waterfall :: [(A->Unit)->Unit, A->(B->Unit)->Unit] -> (B->Unit)->Unit
배열을 다시 풀어보면,
async.waterfall' :: ((A->Unit)->Unit) -> (A->(B->Unit)->Unit) -> (B->Unit)->Unit
슬슬 뭔가 보이기 시작한다. type Cont A = (A->Unit)->Unit
이라고 보면..
async.waterfall' :: Cont A -> (A->Cont B) -> Cont B
즉, monad의 bind와 같은 역할을 하는 함수이다.
원래의 async.waterfall
은 Array에 담긴 Cont를 monad bind로 모두 연결시키는 것이다.