ReScript 공식문서로 ReScript 훑어보기 (2)

본 포스트는 ReScript 공식문서를 스터디하며 정리한 포스트 입니다.
포스트를 작성하며 작성한 코드는 여기에서 확인할 수 있습니다.

Let Binding

ReScript에서의 Let Binding은 다른 언어에서의 변수 선언이라고 할 수 있다.

let gretting = "hello!"
let score = 10
let newScore = 10 + scores

위의 코드와 같이 let 키워드를 사용해 값을 이름에 바인딩한다.

Block Scope

ReScript에서는 {}를 이용해 바인딩 범위를 지정할 수 있다. 블록 스코프 안에서 선언된 변수는 밖에서 접근할 수 없다.

let message = {
  let part1 = "hello"
  let part2 = "world"
}
// part1과 part2에 접근할 수 없다.

블록 스코프의 마지막 줄의 값은 암시적으로 반환된다.

let message = {
  let part1 = "hello"
  let part2 = "world"
  pert1 ++ " " ++ part2
}

따라서 위 코드의 message 변수는 "hello world"의 값을 갖게 된다.

// Generated by ReScript, PLEASE EDIT WITH CARE
'use strict';


var message = "hello world";

exports.message = message;
/* No side effect */

JavaScript로 컴파일된 결과는 위와 같으며 message 변수가 "hello world"인 것을 확인할 수 있다.

Design Decisions

ReScript의 if, while 구문과 함수는 모두 동일한 블록 스코프 지정 매커니즘을 사용한다.

let displayGretting = true
if displayGretting {
  let message = "Enjoying the docs so far?"
  Js.log(message)
}
// message는 여기에서 접근할 수 없다.

앞에서 설명한 블록 스코프와 동일하게 모두 {}를 이용해 블록 스코프를 지정한다.

Binding Are Immutable

Let Binding은 불변하며 “바뀔 수 없음”이라고 한다. 이런 불변함은 타입 시스템이 다른 언어보다 훨씬 더 많은 것을 추론하고 최적화할 수 있도록 돕는다.

Binding Shadowing

바인딩이 불변하다는 것은 비현실적으로 들릴 수 있다. 바인딩된 값을 변경하기 위해서 사용할 수 있는 방법으로는 2가지가 있다.

  1. 변수의 값을 변경하는 것이 아닌 경우가 많다는 것을 깨닫는 것이다.

아래의 JavaScirpt 패턴으로 예를 들 수 있다.

var result = 0;
result = calculate(result);
result = calculateSomeMore(result);

위 코드는 아래와 같이 작성해도 동일하게 동작하며 결과가 왜곡되지 않는다.

var result1 = 0;
var result2 = calculate(result1);
var result3 = calculateSomeMore(result2);

위 코드는 ReScript로 작성해도 확실히 동일하게 동작한다.

let result1 = 0
let result2 = calculate(result1)
let result3 = calculateSomeMore(result2)

JavaScript로 컴파일된 결과물 또한 위의 바로 위의 JavaScript 코드와 동일할 것이다.

  1. 바인딩에 사용한 이름을 재사용하면 동일한 이름의 이전 바인딩이 가려진다.
let result = 0
let result = calculate(result)
let result = calculateSomeMore(result)

위와 같이 코드를 작성해도 동작하며 JavaScript로 컴파일된 코드는 아래와 같다.

var result = calculate(0);
var result$1 = calculateSomeMore(result);

추천하는 방법은 아니지만 아래의 코드 또한 유효하며 동작한다.

let result = "hello"
Js.log(result) // prints "hello"
let result = 1
Js.log(result) // prints 1

위의 코드는 JavaScript로 컴파일이 되면 result 변수는 마지막에 선언한 하나만 남게 된다.

var result = 1;
console.log("hello");
console.log(1);

실질적인 변형은 없으며 참조하는 바인딩은 위쪽에 가장 가까운 바인딩이 된다. 만약 값을 전달하거나 많은 코드들에 의해 진짜 변형이 필요한 경우 ReScript는 약간 더 무거운 변형 기능을 제공한다.

Private Let Bindings

Private let bindingns은 7.2 버전 릴리즈에 도입되었다. ReScript의 모듈 시스템에서 모든 필드들은 기본적으로 public으로 사용된다. 값을 숨기는 유일한 방법은 public 필드와 필드 타입을 나타내는 별도의 시그니처를 작성하는 것이다.

module A: {
  let b: int
} = {
  let a = 3
  let b = 4
}

위와 같이 모듈을 작성하게 될 경우 변수 int타입의 변수인 b만 public필드로 사용된다. JavaScript로 컴파일된 코드는 아래와 같다.

// Generated by ReScript, PLEASE EDIT WITH CARE
'use strict';


var A = {
  b: 4
};

exports.A = A;
/* No side effect */

ReScript의 모듈 A가 컴파일된 JavaScript 객체 A에서 a 필드에 관한 데이터는 없이 컴파일 된 것을 볼 수 있다. 또한 %private를 이용해 직접적으로 private 필드를 지정할 수 있다.

module C = {
  %%private(let a = 3)
  let b = 4
}

%private는 파일 수준 모듈에도 적용되게 되며 컴파일된 결과는 시그니처를 제공하는 방법과 동일하다.

%private를 사용하는 방법보다는 시그니처를 제공하는 방법은 별도의 컴파일 유닛을 더 잘 제공하며 문서화에도 적합해 권장된다. 하지만 %private를 이용하는 방법은 아래와 같은 상황에서 유용하다.

  • 코드 제너레이터 : 일부 코드 제너레이터는 일부 값을 숨기려하지만 코드 제너레이터가 public 필드의 타입을 합성하는 것은 어렵거나 시간이 오래걸리게 된다.
  • 빠른 프로토타이핑 : 빠르게 프로토타이핑을 진행할 경우 값을 숨기고 싶지만 인터페이스 파일이 안정적이지 않을 때 편의를 제공한다.

Written by@Minsu Kim
Software Engineer at KakaoPay Corp.