ReScript in Korean

파이프

원문

리스크립트는 파이프 연산자(->)를 지원합니다. 파이프를 이용하면 a(b)b->a와 같이 작성할 수 있습니다. 파이프는 단순히 방향을 바꿔주는 문법일 뿐이어서 사용할 때 별도의 성능 저하는 없습니다.

이 문법이 유용한 이유를 살펴보겠습니다.

validateAge(getAge(parseData(person)))

위의 코드는 읽기 어렵습니다. 왜냐하면 가장 안쪽 코드를 찾은 뒤 그곳에서부터 읽어야하기 때문입니다. 하지만 파이프를 사용한다면 다음과 같이 바꿀 수 있습니다.

person
->parseData
->getAge
->validateAge

위에서 설명한 것과 같이 person->parseDataparseData(person)로 변환됩니다. getAgevalidateAge 또한 마찬가지입니다.

파이프는 함수가 인자를 한개 이상 넘겨받을 때도 동작합니다

a(one, two, three)

위 코드는 아래와 같습니다.

one->a(two, three)

또한 파이프는 이름이 있는 인자(labeled arguments)에서도 동작합니다.

자바와 같은 객체 지향 프로그래밍 언어에서는 myStudent.getName과 같이 접근할 수 있습니다. 이로써 얻어지는 좋은 가독성은 객체 지향 프로그래밍의 장점입니다. 파이프를 사용하면 리스크립트에서도 getName(myStudent)myStudent->getName처럼 변경해 가독성을 높일 수 있습니다.

팁과 트릭

파이프를 남용하지 마세요. 파이프는 목적이 아니라 수단일 뿐입니다. 때때로 경험이 없는 개발자들은 파이프를 사용할 것이라 생각하고 API의 모양을 설계하는데, 좋지 않은 접근입니다.

자바스크립트 메소드 체이닝

이 파트는 바인딩에 대한 이해가 필요합니다.

자바스크립트에서 함수를 연쇄적으로 호출할때가 있습니다.

const result = [1, 2, 3].map(a => a + 1).filter(a => a % 2 === 0);
asyncRequest()
.setWaitDuration(4000)
.send();

위의 코드를 리스크립트로 작성하기위해 바인딩을 합니다.

@bs.send external map: (array<'a>, 'a => 'b) => array<'b> = "map"
@bs.send external filter: (array<'a>, 'a => bool) => array<'a> = "filter"
type request
@bs.val external asyncRequest: unit => request = "asyncRequest"
@bs.send external setWaitDuration: (request, int) => request = "setWaitDuration"
@bs.send external send: request => unit = "send"

그리고 아래와 같은 리스크립트 코드를 작성합니다.

let result = Js.Array2.filter(
Js.Array2.map([1, 2, 3], a => a + 1),
a => mod(a, 2) == 0
)
send(setWaitDuration(asyncRequest(), 4000))

바인딩 함수를 호출하면서 가독성이 안좋아졌는데, 파이프를 사용하면 자바스크립트 메소드 체이닝과 동일한 모양새로 바꿀 수 있습니다.

let result = [1, 2, 3]
->map(a => a + 1)
->filter(a => mod(a, 2) == 0)
asyncRequest()->setWaitDuration(4000)->send

배리언트와 파이프

배리언트 생성자도 파이프와 함게 사용할 수 있습니다.

let result = name->preprocess->Some

위의 코드는 아래처럼 변경됩니다.

let result = Some(preprocess(name))

참고로, 배리언트 생성자를 함수처럼 사용하는 것은 여기를 제외하고 다른 곳에서 동작하지 않습니다.

파이프 플레이스홀더

플레이스홀더는 언더스코어(_)를 이용해 작성할 수 있습니다. 이것은 해당 인자를 나중에 채우겠다는 의미입니다. 아래 두 함수는 의미가 같습니다.

let addTo7 = (x) => add3(3, x, 4)
let addTo7 = add3(3, _, 4)

때때로, 파이프가 인자의 첫번째 위치하는 값을 가리키게 하고 싶지 않을 수 있습니다. 이럴 때, 파이프로 전달하고자 하는 위치에 플레이스홀더를 두면 됩니다.

getName(input)
->namePerson(personDetails, _)

위의 코드는 아래와 같이 컴파일 됩니다.

var __x = getName(input);
namePerson(personDetails, __x);

파이프를 사용할 때, 전달하고 싶은 인자의 위치에 플레이스홀더를 두는 것은 이름이 있는 인자도 작동합니다.

getName(input)
->namePerson(~person=personDetails, ~name=_)

삼각 파이프 (Deprecated)

아마도 여러 코드에서 삼각 파이프(|>)를 사용하는 것을 볼 수 있을 것입니다. 파이프(->)와는 다르게 삼각 파이프(|>)는 가장 마지막 인자의 위치에 값을 전달합니다. 즉, a |> f(b)f(b, a)와 같습니다. 삼각 파이프는 더 이상 사용하지 않습니다.1 이유는 Javier Chávarri의 Data-first and Data-last comparison 포스트를 참고하세요.


  1. 역주) 삼각 파이프는 최신 버전(v8.4.2) 리스크립트 컴파일러에서도 잘 동작하고, 아직 사용할 수 있는 문법입니다. 하지만 공식적으로 삼각 파이프의 Deprecated를 선언함으로써, 점차 사용을 줄여나가는 한편, 리스크립트에서는 data-first를 추구함을 드러낸다고 이해하면 좋을 것 같습니다. 그 예로 표준 라이브러리인 Belt의 인터페이스 또한 파이프(->)에 적합하게 만들어져 있습니다.