7월, 2015의 게시물 표시

[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인 구조체이다.

[ECMAScript 6] const - 상수 선언하기

>const 는 >let 과 같이 ECMAScript 6 에 도입된 block 단위 상수 선언문이다. 중복으로 선언할 수 없고, 선언 전에 사용할 수 없다는 것은 >let 과 같다. 거기에 >const 는 추가적인 제약이 더 붙는다. 우선 >const 로 선언된 이름에는 값을 재할당할 수 없다. 이는 문법적으로 에러로 처리한다. 따라서 >const 에 값을 할당하는 구문을 실행할 때 발생하는 것이 아니라 >const 에 값을 할당하는 구문이 있는 함수가 선언될 때 에러가 발생한다. 또한, >const 를 이용해서 상수를 선언할 때는 언제나 값을 초기화해주어야 한다. >const 로 선언된 상수에 값을 할당하지 못한다는 것을 생각하면, 당연한 일이다. 이 또한 문법 에러로, 초기화하지 않는 >const 를 선언할 때가 아니라, 선언하는 구문이 있는 함수를 선언할 때 에러가 발생한다. 하지만 >const 도 상수 선언을 위한 완벽한 해결책은 아니다. >const 로 선언한 상수에는 값을 재할당할 수 없지만, 상수임에도 불구하고 값을 변경시킬 수 있기 때문이다. >const 로 선언한 상수는 어디까지나 값의 재할당을 막을 뿐, 그 값을 보호해주지 않는다. >const가 완벽한 해결책인 것은 아니지만 , 한계를 알고 적절하게 사용하면 좀 더 안정적이고 가독성 있는 코드를 작성할 수 있다.

[ECMAScript 6] let - block 단위 스코프

ECMAScript 6 에서는 기존의 function scope였던 >var 이외에 >let 이라는 block scope 변수 선언을 지원한다. >let 을 통한 변수 선언은, >var 를 통한 변수 선언과 다르게 hoisting 하지 않는다. 즉, 변수가 선언된 이후부터 변수가 유효하고 그전에는 해당 변수를 사용할 수 없다. hoisting을 없앤 것뿐 아니라 그 외의 실수하기 쉬운 부분을 에러로 처리하여 좀 더 안전한 코드를 작성할 수 있도록 하였다. 예를 들면, ECMAScript 5 에서는 hoisting 된 변수를 실수로 선언 전에 사용할 경우 그 변수는 >undefined 가 된다. 하지만 >let 을 사용하면, 변수를 hositing 하지 않을 뿐 아니라, 변수를 선언한 블록 안에서 선언 전에 해당하는 이름을 사용하는 것을 에러로 처리한다. 또한, 이전에는 같은 스코프, 다시 말해서 같은 함수 안에서 변수의 선언문이 여러 개 있는 것이 정상적인 구문으로 처리되었다. 하지만 >let 을 사용한다면 같은 스코프에서 중복으로 선언하는 것이 에러로 처리된다. 아쉬운 점은 위와 같은 에러가 함수의 선언에서 발생하는 것이 아니라, 실제로 그 구문을 실행할 때 발생한다는 것이다. 따라서 여전히 높은 커버리지의 테스트를 작성해야 안전한 코드라고 보장할 수 있다. 하지만 예전처럼 예상하지 못한 >undefined 가 나와서 문제가 발생한 부분이 아닌 다른 곳부터 추적해가야 할 일은 없어졌다. 또 다른 문제는 babel 에서 다르게 동작한다는 것이다. 파이어폭스나 크롬 등 모던 브라우저나 io.js는 ECMAScript 6를 지원하지만, 오래된 버전의 IE나 node.js 등에서는 아직 >let 을 지원하지 않는다. 그런 환경에서는 babel을 사용해야 하는데, babel에서는 중복된 선언이나, 선언 전에 사용하는 것을 에러로 처리하지 않는다. 하지만 조만간 node.js에서도 >let 을 지...

[Monad] 사용 예제 - Option : 존재하지 않음을 표현하기

이미지
모나드는 많은 방식으로 사용되지만, 그중에서 Option 타입부터 설명하도록 하겠다. 그 이유는 Option 타입이 가장 기본적인 모나드이고, 가장 많이 사용되는 모나드이기 때문이다. Option 타입은 Haskell 및 몇몇 언어에서는 Maybe 모나드로 불리고, 언어 대부분에서는 Option 타입이라고 불린다. 이 글에서는 그냥 많이 사용되는 Option 타입이라는 이름을 쓰도록 하겠다. Option 타입이 해결하고자 하는 문제는 값이 존재하지 않음을 런타임 에러가 발생할 가능성 없이 표현하는 것이다. C++, C#, Java 등 기존의 많은 언어는 값이 존재하지 않음을 표현하기 위해서, null point를 사용하였다. 그리고 이 null point 문제는 컴파일 타임에 잡을 수 없는 NullPointerException 을 발생시키기 때문에 조심해서 사용해야 했다. 이런 문제를 해결하기 위해 아무런 동작을 하지 않는 객체를 만드는 Null Object pattern 같은 디자인 패턴을 이용하거나, null check를 한 겹 감싼 클래스를 만들거나 해서 문제를 최소화하고 있으나, 여전히 문제를 완벽하게 해결할 수는 없었다. Option 타입은 이에 대한 완벽한 해결책을 제공한다. Option 타입은 하나의 타입 파라미터를 받아, 그 타입의 값을 가지고 있을 수도 있고, 없을 수도 있다. Int 타입을 타입 파라미터로 받았다면, 타입은 Option[Int] 가 되며, String 타입을 타입 파라미터로 받았다면, Option[String] 이 된다. 즉, T 타입을 타입 파라미터로 받은 Option 은 Option[T] 가 된다. 이를 간단히 표현하기 위해서 T ? 같은 방식으로 표현하기도 한다. Option[T] 타입의 값은 T 타입의 값을 가지고 있을 수도 있고, 아무런 값이 없을 수도 있다. 이렇게 말하면 단순한 nullable과 다를 게 없어 보인다. 하지만 Option 은 두 상태를 다른 타입으로 분리함으로써...

[JavaScript] undefined 이해하기

JavaScript에는 3가지 undefined가 존재한다. 타입으로서의 undefined 우선 undefined 는 타입이다. ECMA Script 5까지는 다음과 같은 6가지 1) Built-in type이 있었다. number boolean string object null undefined undefined는 그중 하나다. 값으로서의 undefined undefined 는 undefined 타입의 유일한 값이다. 우선 값이 할당되지 않은 변수 혹은 값이 할당되지 않은 프로퍼티는 undefined 가 된다. 또한, 리턴문이 없는 함수나 리턴하는 값이 없는 리턴문으로 끝나는 함수의 실행 결과도 undefined 가 된다. 혹은 전역 프로퍼티인 undefined 를 통해서 undefined 값을 얻을 수 있다. 전역 프로퍼티 undefined global context에는 undefined 라는 이름의 프로퍼티가 설정되어 있다. 이 undefined 는 undefined 인 값을 가진다. ECMAScript 5 이후로 이 프로퍼티는 non-configurable이고, non-writable로 설정되어 있다. 따라서 변경할 수 없다. 1) ECMAScript 6에는 Symbol type 이 추가되었다.

Monad는 무엇인가

모나드는 하스켈의 성공(?)과 함께 다른 언어에도 유행처럼 퍼져나갔다. 하지만 그 배경이 되는 이론이 너무 복잡하고 수학적이라 많은 사람이 하스켈을 맛만 보다 떠나게 된 이유가 되었고, 다른 언어들에서도 모나드라는 것은 금지어가 되다시피 하였다. 하지만 어렵다고 모른 채로 살기에는 모나드는 너무 유용하다. 아니 유용한 정도가 아니라 이미 많은 곳에 사용되고 있다. 그리고 사실 모나드는 쉽다. 아니 어렵긴 어렵다. 그 배경 지식인 카테고리 이론은 어려운 것 맞다. 하지만 그걸 알 필요 없다. 언제는 프로그래머들이 함수가 무엇인지 수학적으로 이해하고 사용하였나? 카테고리론을 이해하지 못해도 모나드는 얼마든지 사용할 수 있다. 이제 슬슬 모나드가 무엇인지 궁금해졌을 것이다. 이쯤에서 한 문장으로 정리해서 설명했으면 좋겠지만, 아쉽게도 그건 좀 어렵다. 아마 이런 모습도 사람이 모나드를 이해하기 어려워하는 이유일 것이다. 그래도 최대한 풀어서 설명하면 모나드는 다음과 같다. 모나드는 다른 타입을 인자로 받는 타입이다. 모나드는 타입이다. 기본적으로 모나드는 int나 string 같은 타입이다. 다른 점이 있다면, 모나드는 타입을 인자로 받는다. C++에 익숙한 사람이라면, template class 를 다른 generic class 를 지원하는 언어에 익숙한 사람이라면 generic class를 생각하면 된다. 편의에 따라 앞으로 T 라는 타입을 받은 모나드 M 을 M[T] 라고 표현하겠다. 모나드 타입의 값을 생성하는 함수가 있어야 한다. 모나드는 임의 타입의 값을 받아서 그 타입을 인자로 받은 모나드 타입의 값을 반환하는 함수가 있어야 한다. 다시 말하면, T 타입의 값을 받아서 M[T] 타입의 값을 반환하는 함수가 있어야 한다. 이는 하스켈에서 return operator라고 불리는 함수이다. 혹은 unit operator라고 부른다. 다른 모나드 타입으로 진행하는 함수가 있어야 한다. 이는 하스켈에서는 >>= 라고 쓰이...

[npm] publish 하기 전에 테스트하기

npm publish 라는 명령어를 통해 내가 만든 라이브러리를 npm 을 통해 배포할 수 있다. 보통의 경우라면 문제없다. 하지만 TypeScript 나 CoffeeScript 를 이용하여 컴파일된 라이브러리를 배포하거나, webpack 같은 것을 이용해서 라이브러리들을 패킹해서 배포할 경우 npm publish 를 하는 것은 마음 놓고 할 수 있는 작업이 아니다. 배포 전에 명령어를 수행하기 위해 prepublish에 스크립트 를 저장하거나, npmignore 에 배포하지 않을 파일들을 추가하거나 하는데, 이런 것들이 제대로 되어 있는지 실제 npm에 올리기 전에는 알 수 없기 때문이다. 그럴 때 사용하기 좋은 커맨드가 npm pack 이다. npm pack 을 이용하면, 제대로 된 파일들을 배포할지 확인할 수 있다. npm pack 을 실행하면 prepublish를 실행시키고, npmignore에 들어있는 파일들이 빠진 파일들이 {라이브러리 이름}-{버젼}.tgz 라는 이름의 압축파일이 만들어진다. 그러면 해당 라이브러리를 사용하는 프로젝트를 만들고, npm install {압축파일 경로} 를 실행하면, 실제로 publish된 라이브러리를 설치한 것처럼 라이브러리를 설치하여 테스트할 수 있다.

[Python] Gil과 Python

지난번 에 언급했듯이 CPython 이나 PyPy 는 Global interpreter lock (a.k.a. GIL)을 이용해서 동시에 2개 이상의 스레드가 실행되지 못하게 함으로써 스레드 간 동기화를 보장한다. 하지만 이는 CPython과 PyPy가 thread를 구현하는 방법일 뿐, Python 스펙에는 동시에 2개 이상의 스레드를 실행시키지 말라거나, GIL을 사용하라거나 하는 말은 없다. 그저 CPython과 PyPy가 효율성을 떨어뜨리더라도 GIL을 사용하는 것이 이득이 되는 것이 많다고 생각해서 GIL을 사용하도록 구현한 것뿐이다. 그래서 Python 구현체 중에서 .net framework 위에서 돌아가는 Iron Python 이나 JVM 위에서 올라가는 Jython 의 경우 GIL을 사용하지 않는다.

sfuture - JavaScript에서 concurrent한 프로그램 작성하기

sfuture 는 JavaScript에서 사용할 수 있는 컨커런트한 프로그램을 쉽게 작성할 수 있도록 도와주는 라이브러리다. 이름에서 알 수 있듯이, Scala의 Future를 JavaScript로 포팅한 것으로, 내부적으로 ECMA Script 6 promise를 사용하고 있어서, promise가 구현된 환경(node.js 0.12.0 이상, 대부분의 모던 브라우저)에서는 아무런 디펜던시 없이 바로 사용할 수 있다. 만들게 된 이유는, 전에 rhino engine 을 이용해서 Scala로 작성한 beyond 라는 게임 서버 엔진이 있었는데, 여러 가지 문제가 있어서 이것을 node.js로 포팅하게 되면서 필요하게 되었다. Rhino를 사용할 때는 Java class를 그대로 재사용할 수 있었기에 인터페이스만 수정하는 수준이면 가능했는데, node.js로 포팅해오면서 그럴 수 없게 되었다. 처음에는 Future를 포팅해올지, async등 기존의 라이브러리들을 이용하도록 할지 고민했다. 하지만 async를 실제로 사용을 해보니, 이것도 결국 콜백 헬을 없앨 수 있는 건 아니고, 오히려 코드가 길어질수록, 가독성만 떨어뜨리는 느낌이라서 결국 새로 구현하였다. 구현은 타입스크립트로 되어 있지만, publish 전에 컴파일하여 자바스크립트 파일만 배포한다. 원본 소스를 보고 싶으면, 깃헙 리파지토리 를 보길 바란다.

이 블로그의 인기 게시물

USB 2.0 케이블의 내부 구조

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

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

[Web] SpeechSynthesis - TTS API

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