LiveScript + NodeJS

back call

livescript는 자바스크립트로 컴파일되는 스크립트 언어로, 그 문법적 특징이 하스켈
의 것을 많이 참고하고 있다. 예를 들면 괄호없이 함수를 호출한다거나, 함수 파라미
터간의 컴마를 생략할 수 있다거나 하는 것들이 있다.

한 때 흥미삼아서 조금 만져보다가 관뒀었는데, 갑자기 모 사이트에서 NodeJS 튜토리얼을 몇 개 따라해보려다가1 생각이나서 그 사이트의 튜토리얼 코드들을 라이브스크립트로 코딩해봤더니 이거 썩 괜찮더라.

특히 화살표(->)를 이용한 익명함수 정의 문법2을 이용해서 함수를 인자로 전달하거나 함수가 함수를 리턴하는 것을 좀 더 간단하게 표기할 수 있게 한다.

예를 들어 이벤트핸들러를 등록하는 코드를 생각해보자.

var xhr = new XMLHttpRequest();
...
xhr.addEventListenser('onload', function(complete){
    console.log(complete.target.responseText);
});

라이브 스크립트의 익명함수는

add2 = (x) -> x + 2

와 같은 식으로 표현하므로, 라이브스크립트 문법으로는 다음과 같이 쓸 수 있다. (가장 얌전하게)

xhr = new XML-http-request()
xhr.addEventListener 'onload' (complete) -> console.log(complete.target.responseText)

한 줄에 적기가 너무 길면, 함수 본체 부분을 들여쓰면서 줄바꿈하면 된다.

xhr = new XML-http-request!
xhr.addEventListener 'onload' (complete) ->
    console.log complete.target.responseText

그런데 라이브스크립트는 backcall이라는 문법을 지원한다. 위의 익명함수 지정문
법에서 화살표의 방향을 살짝 틀어 바꾼 것이다. 3

complete <- xhr.addEventListener 'onload'
console.log complete.target.responseText

이것이 익명함수 문법과 다른 것은 콜백처럼 파라미터로 넘겨지는 함수를 코드상에서
는 전혀 nested 되지 않고 표현한다는 점이다.

기존의 자바스크립트 문법은 파라미터 삽입 위치에서 on-the-fly로 함수를 정의하는 형태를 직설적으로 보여주었다.

하지만 실제 콜백의 동작은 원래의 함수가 대부분의 실행을 마친 후, 그 함수 내에서 얻게된 어떠한 산출물을 사용자가 정의한 함수에 넘겨주면서 호출하는 형태이다. 따라서 실질적인 코드의 진행은 원래함수의 본체 -> 콜백함수인 셈이다.

이 back call 문법은 이러한 콜백의 호출을 마치 하스켈의 do 블럭 문법처럼 만든 것이다. 즉, 먼저 실행되는 원래 함수의 중간결과 (콜백함수의 인자가될)를 모나드값으로본다. 뭔가 어떤 상태 아래에 있는 간접적인 존재로 보는 것이다. 그리고 그 어떤 상태에 있는 값을 현재 범위의 변수에 바인딩한다. 그런 다음 그 이후의 코드에서는 이것을 현재 스코프의 지역변수인 것처럼 쓰는 것이다.

물론 라이브스크립트는 실제로는 바닐라 자바스크립트로 컴파일되므로, 모나드값 바인딩 같은 일은 일어나지 않는다. 다만 함수형 언어가 사용하는 그러한 문법적 장치를 거꾸로 자바스크립트 문법으로 치환할 수 있는 규칙을 적용한 것 뿐이다. 하지만 이는 단순히 문법적인 장식 이상의 효과를 가져오는데, 특히 콜백 천국인 NodeJS에서는 특별히 유용하다.

예를 들어서 특정한 웹사이트의 소스코드를 읽어와서 파일로 저장하는 경우를 생각해보도록 하자.

require { \fs \http }

do
    res <- http.request do
        host: \www.naver.com
        path: \/
    data = ""
    do
        chunk <- res.on \data
        data += chunk
    do
        <- res.on \end
        err <- fs.writeFile \output.html data.toString!
        if err then console.error err
        else console.log "Successfully saved."
.end!

단순히 괄호가 없는 것만으로도 읽기가 편한 가운데, 들여쓰는 단계가 대폭 줄어들었다. 실제로 똑같은 동작을 하는 자바스크립트 코드를 작성해도 역시 라인수는 크게 늘어나지 않는다. 다만 그 실상이 아래와 같을 뿐이다.

var fs = require('fs');
var http = require('http');
http.request({
    host: 'www.naver.com',
    path: '/'
}, function(res) {
    var data = "";
    res.on('data', function(chunk) {
        data += chunk;
    });
    res.on('end', function() {
        fs.writeFile('output.html', data.toString(), function(err) {
            if (err) {
                console.error(err);
            } else {
                console.log("Successfully saved.");
            }
        });
    });
}).end();

뭔가 괄호를 아직 다 닫지 않았는데도 콜백을 또 쓰고 또 써야하는 일이 많을 것 같은
NodeJS라면 livescript를 도입해보는 것이 결코 나쁜 선택은 아닐 것 같다.

이렇게 쓰고보니, mithril의 view함수 내부도 괄호지옥이라 도입했을 때 시너지가
상당할 것 같다.


  1. 파이썬, 하스켈처럼 괄호 많이 안쓰고 세미콜론 없는 언어들을 좋아합니다. 
  2. 이 기능은 ECMA6에 추가될 예정이다. var f = (x) => { return x + 1; };과 같은 식이다. 
  3. 실제로 아래의 코드는 바로 위의 코드와 완전히 똑같은 자바스크립트 코드로 번역된다.