ReScript in Korean

Null, Undefined 그리고 Option

원문

리스크립트 자체는 null 또는 undefined에 대한 개념이 없습니다. 이건 버그의 전체 범주를 제거하는 정말 훌륭한 일입니다. 더 이상 undefined is not a function, 그리고 cannot access someAttribute of undefined 오류는 없습니다!

그러나 잠재적으로 존재하지 않는 값의 개념은 여전히 유용하며, 리스크립트에서도 안전하게 존재합니다.

리스크립트는 option 타입으로 값을 감싸서 존재하는 값과 존재하지 않는 값을 표현합니다. option 타입의 표준 라이브러러리 정의는 다음과 같습니다:

type option<'a> = None | Some('a)

즉, "option 타입의 값은 None(존재하지 않음)이거나 Some으로 감싸진 실제 값"을 의미합니다.

참고 option 타입은 단지 일반적인 배리언트 입니다.

예제

여기 평범한 값이 있습니다.

let licenseNumber = 5

"maybe null"의 개념1을 나타내려면 값을 감싸서 option 타입으로 만들면 됩니다. 조건문을 추가하고 licenseNumber를 option 타입으로 만들겠습니다.

let licenseNumber =
if personHasACar {
Some(5)
} else {
None
}

이제 licenseNumber는 option 타입입니다. 나중에 다른 코드가 이 값을 받으면 패턴 매칭을 통해 Some('a)None두 값을 모두 처리해야합니다.

switch licenseNumber {
| None =>
Js.log("The person doesn't have a car")
| Some(number) =>
Js.log("The person's license number is " ++ Js.Int.toString(number))
}

위 예제에서는 일반적인 숫자를 option 타입으로 바꾸고 None 케이스에 대한 처리를 강제함으로써 리스크립트는 개념적으로 null값을 잘못 처리하거나 처리를 깜빡 잊을 가능성을 효과적으로 제거했습니다! 순수 리스크립트 프로그램에는 null 에러가 없습니다.

자바스크립트의 undefined와 null의 Interoperate

option 타입은 자바스크립트로 컴파일할 때 일반적으로 다음처럼 처리됩니다.

let x = Some(5)
var x = 5;

이렇게 간단히 5로 컴파일 되고,

let x = None
var x;

위 경우에는 undefined로 컴파일 됩니다! 추가적으로 어떤 변수 값이 string 또는 undefined가 될 수 있다면, 타입을 option<string>처럼 지정하면 됩니다. 이럴 경우, Some("ReScript") 또는 None 을 JS로 보내고 올바르게 해석되는것을 기대할 수 있습니다 =)

주의 1

option 에서 undefined의 변환은 완벽하지 않습니다. 왜냐하면 코드를 작성하는 쪽에서 option 값을 조정할 수 있기 때문입니다.

let x = Some(Some(Some(5)))
var x = 5;

이것은 여전히 5로 컴파일되지만, 다음은 문제가 있습니다.

let x = Some(None)
var Caml_option = require('./stdlib/caml_option.js');
var x = Caml_option.some(undefined);

Caml_option.some이 하는 일은 무엇일까요? 왜 x의 값이 undefined로 컴파일되지 않을까요? 간단히 말하자면, polymorphic option 타입('a의 경우 option<'a>)을 다룰 경우, 만약 우리가 특별한 어노테이션을 지정하지 않으면 많은 작업이 까다로워집니다. 이것이 넌센스라도 걱정하지 마세요. 다음 2가지 규칙을 따르세요.

  • 절대로 중첩된 option 값(e.g. Some(Some(Some(5))))을 JS 쪽으로 전달하지 마세요.
  • 절대로 JS에서 오는 값을 option<'a>로 표시하지 마세요. 항상 구체적인 non-polymorphic type을 지정하세요.

주의 2

안타깝게도 많은 경우에 자바스크립트 값은 null 또는 undefined 양쪽일 수 있습니다.(역주. 양쪽 다 존재하지 않는 값의 개념이지만 자바스크립트에서는 null과 undefined가 다른 취급을 받습니다.) 만약 option<int> 타입의 변수가 있을 경우 이 optionNone 처리는 오직 undefined만 체크하고 null에 대해서는 체크하지 않습니다.

해법: 보다 세련되고 정교한 undefined & null Interop

이를 해결하기 위해 우리는 풀기위해 우리는 Js.Nullable 모듈을 제공하고 이것을 통해 보다 정교하게 nullundefined 접근을 할 수 있습니다. 이것은 option 타입과 약간 비슷하게 작동하지만 다릅니다.

예제

JS의 null을 만들기 위해서는 Js.Nullable.null를 사용합니다. JS의 undefined을 만들기 위해서는 Js.Nullable.undefined를 사용합니다. (자연스럽게 None을 사용할수도 있습니다만, 여기의 핵심은 아닙니다. Js.Nullable.*는 작동하지 않습니다)

예를 들어 nullundefined 둘 다 될수 있는 문자열을 받을 경우 다음처럼 타이핑 합니다.

@bs.module("MyConstant") external myId: Js.Nullable.t<string> = "myId"

리스크립트 쪽에서 nullable 문자열을 생성하려면(아마 인터롭을 목적으로 JS 쪽에 전달하는 경우입니다) 다음처럼 하세요.

@bs.module("MyIdValidator") external validate: Js.Nullable.t<string> => bool = "validate"
let personId: Js.Nullable.t<string> = Js.Nullable.return("abc123")
let result = validate(personId)
var MyIdValidator = require('MyIdValidator');
var personId = 'abc123';
var result = MyIdValidator.validate(personId);

여기서 Js.Nullable.return 함수는 문자열을 nullable 문자열로 감싸고 타입 시스템에게 값을 전달할 때 이 값이 단순 문자열이 아니라 null 또는 undefined이 될 수 있다고 추적할수 있게 해줍니다.2

Js.Nullable 과 option 타입간 상호 변환

Js.Nullable.fromOptionoption 타입에서 Js.Nullable.t 타입으로 변경합니다. Js.Nullable.toOption는 그 반대입니다.