[ECMAScript 6] Symbol - 7번째 primitive type

지금까지 자바스크립트에는 number , boolean , string , null , undefined , object 의 6가지 타입밖에 없었다. 그래서 C의 enum 같은 타입이 필요하거나, 일종의 태깅 같은 것을 위해 고유한 값이 필요했을 경우 보통 number 나 string 타입을 이용했다. 하지만 ECMAScript 6에서는 이제 number 나 string 을 이용할 필요가 없다. ECMAScript 6에서는 새로운 타입인 Symbol 타입 이 추가되었기 때문이다. Symbol 타입의 값은 Symbol 함수를 통해서만 생성할 수 있고, 생성자를 통해서 만들 수 없다. Symbol 함수는 인자로 description을 받을 수도 있고, 아무 인자도 받지 않을 수도 있다. 이 인자는 실제로 생성되는 Symbol 에 영향을 주지 않는다. 로깅 등을 위해서 toString 함수를 이용해 string으로 변환할 때, 반영되지만 이는 디버깅을 위해서고, 일반적으로 이 description을 이용할 일은 없다. 같은 description을 이용해 생성한 Symbol 도 실제로는 다른 값을 가진다. 이는 Symbol 타입이 unique함을 보장하기 때문이다. Symbol 타입은 immutability와 unique 함이 보장된다. number 나 string 도 immutability는 보장된다. 하지만 unique 함은 보장되지 않는다. 이것이 Symbol 타입과 number / string 타입과의 차이점이다. 같은 Symbol 을 가지고 오기 위해서는 생성한 Symbol 을 전역 변수로 등록시키고 있어야 한다. 이것을 해주는 게 for 함수이다. Symbol.for() 함수는 key를 인자로 받는다. 이전에 같은 key로 생성한 Symbol 이 있으면 그 Symbol 을 돌려주고, 처음 받은 key면 새로운 Symbol 을 생성하여, 저장한 뒤 돌려준다. 주의해야 할 것은 Symbol.for(key) 함수는

[ECMAScript 6] block 안에서 함수 만들기

JavaScript 함수 선언의 가장 큰 특징은 함수의 선언 위치에 상관없이 언제나 코드의 가장 위에서 함수를 선언한 것처럼 코드가 실행된다는 것이다. 따라서 아래 두 코드는 사실 같은 코드라고 봐도 된다. 이를 function hoisting 이라고 한다. 이 덕분에 함수 선언문보다 앞에서 함수를 사용할 수 있다. 하지만 함수 선언은 언제나 스코프의 가장 윗부분으로 hoisting 된다. 따라서 함수 안에서 선언된 함수는 함수 내에서 언제나 같은 함수를 의미했고, 특정 block 안에서는 다른 함수를 의미하도록 사용할 수 없었다. 하지만 ECMAScript 6에서는 block 단위의 함수 선언을 허용한다. 즉, 위와 같이 if block 안에서만 다른 값을 의미하도록 하는 것이 가능하다. 하지만 아쉽게도 이는 아직 대부분 브라우저나 node.js에서는 구현되지 않았다 . 따라서 블록 단위 함수 선언을 사용하려면 babel.js 를 사용해야 한다.

[ECMAScript 6] 함수 이름 가져오기

자바스크립트는 두 가지 방식으로 함수를 선언할 수 있다. 평범하게 방법은 함수를 선언하여 사용할 수도 있고, 익명함수를 만들어 사용할 수도 있다. ECMAScript 5 까지는 어떻게 만들어지든 둘 사이에는 차이가 없었다. 만들 수 있는 위치나, 함수의 선언 및 할당이 실제로 이뤄지는 위치가 다르기는 하지만, 어쨌든 만들어지고 난 다음에 둘은 아무런 차이가 없었다. 위와 같은 코드는 사실 아래와 같은 코드에 syntax sugar일 뿐이다. 하지만 ECMAScript 6 이후로 둘은 name property라는 다른 점을 가진다. 첫 번째 방식으로 만든 함수는 named function이라고 불리며 name 이라는 프로퍼티를 가진다. 반면에 두 번째 방식으로 만들어진 함수는 anonymous function이라고 불리며 길이가 0인 문자열( "" )을 name property로 가진다.

[ECMAScript 6] fat arrow function

fat arrow function( => )는 ECMAScript 6 에 추가된 익명 함수를 생성하는 새로운 방법이다. 기존의 함수를 만드는 것보다 짧게 함수를 만들 수 있다. 이는 다른 함수에 콜백으로 함수를 넘겨야 하는 경우 요긴하게 사용된다. 하지만 단순히 길이가 짧다는 것만이 arrow function의 장점이 아니다. 오히려 중요한 특색은 arrow function은 this 를 lexical scope 에서 찾는다는 것이다. JavaScript 함수의 가장 큰 특징 중 하나는 this가 dynamic binding 된다는 것이다. 보통은 큰 문제가 되지 않지만, 메소드를 콜백으로 넘겨주어야 하는 경우나 메소드를 변수로 받을 경우 원하는 대로 돌아가지 않았다. ECMAScript 5 에서는 Function에 bind 메소드 가 추가되어 원하는 오브젝트를 this 로 바인드 한 함수를 만들 수 있지만, 매번 이런 작업을 하는 것은 귀찮은 일이다. 이제 ECMAScript 6에서는 이런 경우에 => 을 이용하여 this 가 lexical binding 된 함수를 만들면 된다.

[TypeScript] Type guard - sum type 분리하기

Type guard는 다른 언어에서 보기 힘든 TypeScript만의 독특한 기능으로, 타입 인트로스펙션 을 통해 분기한 블록 안에서 해당 변수의 타입을 한정시켜주는 기능을 말한다. TypeScript를 사용하다 보면, 하나의 변수가 2개 이상의 타입일 가능성이 있는 경우가 자주 생긴다. TypeScript의 본질이 JavaScript이고 이는 동적 타입 언어이기 때문일 것이다. 이를 위해서 TypeScritp는 any 타입을 이용하거나, 조금 더 안전한 사용을 위해서 유니언 타입을 이용한다. 하지만 유니언 타입의 값은 그 값이 될 수 있는 모든 타입이 공통으로 가지는 함수와 프로퍼티만 이용할 수 있고, 모든 타입이 들어갈 수 있는 함수에만 사용 사용할 수 있다. 이런 불편함을 없애기 위해서 나온 기능이 type guard이다. 위의 코드처럼 여러 개의 타입이 될 수 있는 값을 사용하기 전에 인트로스펙션을 이용해서 타입을 확인하고 값을 사용하는 것은 JavaScript에서 볼 수 있는 흔한 패턴이다. 이렇게 타입을 확인하고 나면, 확인한 블록 안에서 그 값은 해당하는 타입이 되는 것이 type guard이다. 하지만 아직 모든 인트로스펙션에 대해서 type guard가 적용되는 것은 아니다. 현재 type guard가 적용되는 경우는 인트로스펙션이 조건문에 들어가는 if 블록과 그에 따라오는 else 블록뿐이다. 위의 예제처럼 if 블록이 반드시 return 하여 그다음은 else 블록과 다를 바 없는 경우에는 type guard가 적용되지 않는다. 게다가 모든 인트로스펙션이 가능한 것도 아니고, instanceof 를 사용하는 경우와 typeof 의 결과가 'number' , 'string' , 'boolean' 이 되는 경우뿐이다. 그래서 underscore 등을 이용해서 타입 체킹을 하는 경우나 인트로스펙션 부분을 함수로 뺀 경우는 type guard가 동작하지 않는다. 이는 TypeS

[Monad] 사용 예제 - Try : 예외 처리하기

이미지
현대의 대부분의 언어는 예외 처리를 위해서 try-catch 시스템을 사용한다. 예외가 발생할 수 있는 코드를 try 블록에 집어넣고, 예외를 throw 하면 catch 블록에서 예외를 잡아서 처리하는 방식으로, 사실상 현대의 언어들이 예외를 처리하는 방식의 de facto라고 할 수 있다. 하지만 try-catch 시스템에는 여러 가지 문제가 있다. 우선 다른 함수를 호출할 때, 어떤 예외가 발생할지 모른다. 그래서 Java 같은 언어는 함수의 시그니쳐에 발생 가능한 예외를 적는 checked exception 이라는 개념을 만들었지만, RuntimeException 은 어떤 예외가 발생할지 모른다거나, 모든 예외를 하나하나 등록하기 귀찮아서 그냥 Exception 이 발생한다고 적거나 하는 이유로 그다지 쓸모없다는 인식이 강하고 C#을 비롯한 다른 언어들에서는 사용되지 않는다. 그저 API 문서에 함수가 어떤 예외를 발생시킬 수 있는지 적을 뿐이다. 게다가 try-catch 시스템은 예외를 던지는 것이기 때문에 컨트롤 플로우가 뛰게 된다. 물론 현대 언어에서는 클로져나 람다 함수가 자주 사용되기 때문에 컨트롤 플로우가 직선적으로 흐르지 않는다. 하지만 try-catch 시스템은 도가 지나치다. 예외를 던지면, 예외를 잡을 때까지 컨트롤 플로우가 거슬러 올라간다. 그래서 try-catch에 의한 예외 시스템을 가독성이라는 측면에서 goto 나 setjmp / longjmp 와 다를 게 없다고 비판하는 사람들도 있다. 이에 비해 Try 모나드를 사용한 예외처리는 좀 더 예측할 수 있고 가독성 있는 코드를 작성할 수 있게 해준다. Try 모나드는 Option 과 마찬가지로 두 모나드의 sum type 이다. 하지만 하나의 타입 파라미터를 받는 Option 과 다르게 타입 파라미터를 두 개 받는다. 이 두 타입은 각각 성공했을 때 결과 타입인 T 와 에러가 발생했을 에러 타입인 E 다. 그래서 Try 모나드는 Try[T, E] 로 표현한다

Phantom type - 구체화 되지 않는 타입 추가하기

팬텀 타입 은 받은 타입 파라미터 중에서 구조체의 선언에 기여하지 않는 타입 파라미터가 존재하는 타입을 말한다. 무언가 존재하는 것 같지만, 만져지는 실체가 없는 것이 유령 같다고 하여 팬텀 타입이라고 불린다. 내가 알기로는 Haskell에서 가장 먼저 도입된 것으로 알고 있다. Haskell이 팬텀 타입을 사용하기 시작한 뒤로 파라메트릭 폴리몰피즘 을 중시하며 강타입 타입 시스템 을 가진 Scala나 Rust 같은 다른 언어에서도 사용된다. 보통 팬텀 타입을 사용하는 이유는 런타임 오버헤드 없이 컴파일 타임에 제약 조건을 추가하기 위해서다. 팬텀 타입에 사용된 타입 파라미터는 구조체의 값으로 사용되지 않기 때문에 구조체의 크기를 증가시키거나, 실행 시에 별도의 정보를 더 들고 다니지 않는다. 그저 컴파일 타임에 타입 체크하는 조건을 강화할 뿐이다. 이에 대한 좋은 예제가 rust by example에 있어서 가지고 왔다. 1) 위의 예제는 Length 라는 구조체를 단위를 타입 파라미터로 받는 클래스로 선언하였다. 하지만 단위에 해당하는 Unit 은 Length 구조체의 어떤 값으로도 사용되지 않는다. 따라서 Length 구조체의 크기는 f64의 크기와 같다. 2) 하지만 Length<Mm> 는 Length<Inch> 와 다른 값이기 때문에 Length<Mm> 타입인 값과 Length<Inch> 타입인 두 값을 더할 수 없다. 1) 예제 코드는 Apache 2.0 라이센스로 배포되는 rust by example 에 나오는 예제를 가지고 왔다. 2) rust는 구조체에 기여하지 않는 타입 파라미터를 만들 수 없어 PhantomData 를 이용했다. PhantomData는 size가 0인 구조체이다.

이 블로그의 인기 게시물

[C++] enum class - 안전하고 쓰기 쉬운 enum

RAII는 무엇인가

Log Aggregator 비교 - Scribe, Flume, Fluentd, logstash

[Python] cache 데코레이터로 최적화하기

[Web] SpeechSynthesis - TTS API