2014-10-31

[Scala] 함수 선언과 호출에서의 괄호 생략


 위에서 보듯이 Scala에서는 인자가 0개인 함수를 선언하거나 호출할 때 괄호를 생략할 수 있다.1) 컴파일 타임에 괄호 생략에 대해 아무런 제약도 하지 않고, 컴파일된 byte code에도 차이가 없다. 그렇다고 아무 괄호나 생략해도 되는 것은 아니다. 언어적으로는 괄호 생략에 관해 아무 제약을 하지 않지만, side-effect가 없을 때만 괄호를 생략하여야 한다.2)

 side-effect가 없는 함수에 대해서만 괄호를 생략하는 것은 함수가 선언된 모양만으로 그 함수가 하는 일을 추측할 수 있기 때문에 코드의 가독성을 높이는 데 도움이 된다. 그렇다면 어째서 컴파일 타임에 side-effect가 없는 함수에 대해서만 괄호를 생략할 수 있도록 강제하지 않을까?
 이유는 간단하다. 컴파일러가 함수가 side-effect가 있는지 없는지 구분할 수 없기 때문이다. Haskelmonad같은 개념을 도입하여 side-effect를 분리해 낼 수 있을지도 모르지만, Scala는 그런 개념을 도입하지 않고, 사용자에게 책임을 넘겼다.

 side-effect는 functional 프로그래밍에서 매우 중요한 부분이기 때문에 이것에 대한 책임을 프로그래머에게 넘겼다는 점에서, Scala의 안 좋은 부분이라고 평하는 사람도 있다. 하지만 나는 이 정도 자유는 프로그래머에게 넘기는 것이 적당하다고 생각한다.

 예를 들어, 무언가 side-effect가 없는 일을 하는 함수가 있을 때 그 함수가 호출되면 logging을 하도록 수정하였다고 생각해보자. 이 함수는 side-effect가 있는 함수라고 봐야 할까? side-effect가 없는 함수라고 봐야 할까? 엄밀한 의미에서 따져본다면 side-effect가 있는 함수라고 분류해야겠지만, 실제 돌아가는 logic을 생각해보면 이 함수를 side-effect가 있다고 구분하는 것은 뭔가 억울(?)하다.

1) http://docs.scala-lang.org/style/method-invocation.html#arity-0
2) http://docs.scala-lang.org/style/naming-conventions.html#parentheses

2014-10-24

Big endian과 little endian

 Endianness는 것은 메모리상에서 byte를 배치하는 방법을 말한다. 크게는 big-endian과 little-endian으로 구분된다.

Big endian

출처 : 위키피디아1)
 우선 big-endian은 가장 큰 byte(most significant byte, a.k.a. MSB)가 가장 앞에 나오는 방식이다. 일반적으로 사람이 사용하는 방식이라고 생각하면 된다.
 Big-endian은 사람이 흔하게 사용하는 방식이기 때문에 big-endian으로 기록되어 있는 값은 사람이 읽기 쉽고, bit order와 byte order, word order까지 일관성 있다.

Little endian

출처 : 위키피디아2)
 Little-endian은 가장 작은 byte(least significant byte, a.k.a. LSB)가 가장 앞에 나오는 방식을 의미한다. 다시 말해 위의 그림처럼 0x0A0B0C0D를 메모리에 올리고, 메모리를 순서대로 읽으면 0x0D0C0B0A가 나온다는 것이다.
 처음 little-endian을 보면 이게 도대체 뭐하는 짓인가 싶은데 현대의 컴퓨터들은 대부분 little-endian을 이용한다. 왜냐하면 인텔이 머신을 이렇게 만들었으니까. 그렇다면 인텔은 왜 little-endian을 사용할까?

 Little-endian이 컴퓨터에서 컴퓨터의 연산을 아주 미묘하게 빠르게 만들어준다. little-endian을 사용하면 아무런 연산 없이 사이즈가 다른 변수로 casting 할 수 있다. 또한, 덧셈할 때도 little-endian을 사용하는 게 chip을 설계하기 쉽다. 언제나 LSB의 offset이 0으로 같아서 몇 바이트 변수를 더하더라도 언제나 offset이 0인 byte부터 시작하여 더하여 올릴 수 있기 때문에 쉽게 가산기를 구현할 수 있다.

 근데 이게 사람이 메모리를 읽기 어렵게 만들면서까지 성능을 올릴 필요가 있는지는 모르겠다. 개인적인 생각으로는 이렇게까지 해서 성능을 올리고 칩을 간단하게 만들 필요가 있을지 잘 모르겠다. 하지만 인텔이 little-endian을 사용하고 있으니 앞으로도 계속 사용될 듯하다.

1) http://en.wikipedia.org/wiki/Endianness
2) http://en.wikipedia.org/wiki/Endianness

 혹시 endianness에 대해 더 궁금한 것이 있다면 Danny Cohen이 쓴 ON HOLY WARS AND A PLEA FOR PEACE를 읽어보면 도움이 될 것이다.

2014-10-08

[C#] extension method - 존재하는 class 확장하기.

 지난 글에서 이미 존재하는 class에 새로운 함수를 추가해주는 Scala의 implicit class라는 기능을 소개했다. Scala의 implicit class는 라이브러리에서 제공해주어 내가 수정할 수 없는 class를 확장할 때 유용하게 사용되며, 코드의 일관성을 유지하기 위해 원래 class에는 최소한의 기능만을 구현하고, 추가적인 구현은 종류별로 여러 implicit class를 구현하는 방식으로 사용되기도 한다. class의 선언과 구현 detail을 분리할 수 있게 해주는 여러모로 재밌는 기능이다.

 C#은 이런 기능을 extension method라는 방식으로 구현하였다.
 Extension method가 없던 시절(사실 없던 시절은 꽤 오래전 이야기다. Extension method가 유명하지 않아서 그렇지 vs2008부터 들어갔던 기능이다.)에는 확장하고 싶은 class를 상속받아 새로운 class를 만들거나, wrapper class를 만들거나, static method를 만들어 객체를 넘기는 방식으로 구현하여야 했다.
 하지만 이런 방식들은 전부 문제가 있다. 우선 상속으로 새로운 class를 구현하는 방법은 확장할 클래스가 sealed class일 경우 사용하지 못한다. wrapper class를 만드는 방법은 새로 만드는 함수 이외에 다른 함수들을 wrapper class로 연결해 주어야 해서 코드가 매우 boilerplate 해진다. 무엇보다 이 두 방법은 모두 하고자 하는 일에 비해 너무 많은 코드를 작성해야 한다. 마지막 static method를 만드는 방법은 함수를 호출하는 모양이 달라서 코드의 일관성이 없어진다.
 하지만 extension method를 사용하면 이런 문제를 쉽게 해결할 수 있다.

 Extension method를 정의하는 것은 간단하다. 확장하고자 하는 method를 static class의 static method로 정의하면서 첫 번째 인자에 this modifier를 붙이면 된다.
 전체적으로 작업할 코드의 양은 static method를 만들어서 Random 객체를 인자로 넘기는 함수를 만드는 것과 크게 다르지 않다. 단순히 'this'라는 4글자의 modifier가 추가된 정도이다. 이렇게 extension method를 정의하면 첫 번째 parameter가 instance로 쓰이고 나머지 인자들은 함수의 인자로 사용된다.

 C# library 중 extension method를 사용하는 대표적인 라이브러리는 Linq다. Linq는 IEnumerable을 extension method로 확장하여 C#의 IEnumerable을 상속받는 collection들을 전부 일관된 형식으로 사용할 수 있게 해줬다.

 앞에서 말했듯이 extension method라는 기능이 c#에 들어간 지는 매우 오래됐다. 하지만 존재 자체를 모르는 사람들도 많다. 왜냐하면, extension method를 사용하는 것이 장려되지 않기 때문이다. MSDN의 guide 문서에서도 가능하면 상속을 이용하고, 최대한 extension method 사용을 피하라고 말하고 있다. Extension method를 너무 남용하면, 오히려 가독성이 떨어지게 되기 때문이다.
 그래서 Linq처럼 하나의 Domain-specific language(a.k.a. DSL)을 새로 만들어야 하는 경우가 아니면 잘 쓰이지 않는다.