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

위에서 보듯이 Scala에서는 인자가 0개인 함수를 선언하거나 호출할 때 괄호를 생략할 수 있다. 1) 컴파일 타임에 괄호 생략에 대해 아무런 제약도 하지 않고, 컴파일된 byte code에도 차이가 없다. 그렇다고 아무 괄호나 생략해도 되는 것은 아니다. 언어적으로는 괄호 생략에 관해 아무 제약을 하지 않지만, side-effect가 없을 때만 괄호를 생략하여야 한다. 2) side-effect가 없는 함수에 대해서만 괄호를 생략하는 것은 함수가 선언된 모양만으로 그 함수가 하는 일을 추측할 수 있기 때문에 코드의 가독성을 높이는 데 도움이 된다. 그렇다면 어째서 컴파일 타임에 side-effect가 없는 함수에 대해서만 괄호를 생략할 수 있도록 강제하지 않을까? 이유는 간단하다. 컴파일러가 함수가 side-effect가 있는지 없는지 구분할 수 없기 때문이다. Haskel 의 monad 같은 개념을 도입하여 side-effect를 분리해 낼 수 있을지도 모르지만, Scala는 그런 개념을 도입하지 않고, 사용자에게 책임을 넘겼다. side-effect는 functional 프로그래밍에서 매우 중요한 부분이기 때문에 이것에 대한 책임을 프로그래머에게 넘겼다는 점에서, Scala의 안 좋은 부분이라고 평하는 사람도 있다. 하지만 나는 이 정도 자유는 프로그래머에게 넘기는 것이 적당하다고 생각한다. 예를 들어, 무언가 side-effect가 없는 일을 하는 함수가 있을 때 그 함수가 호출되면 logging을 하도록 수정하였다고 생각해보자. 이 함수는 side-effect가 있는 함수라고 봐야 할까? side-effect가 없는 함수라고 봐야 할까? 엄밀한 의미에서 따져본다면 side-effect가 있는 함수라고 분류해야겠지만, 실제 돌아가는 logic을 생각해보면 이 함수를 side-effect가 있다고 구분하는 것은 뭔가 억울(?)하다. 1) https://docs.scala-lang.org/style/method-invocation

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부터 시작하여 더하여 올릴 수 있기 때문에 쉽게 가산기를 구현할 수 있다. 근데 이게 사람이 메모리를 읽기 어렵게 만들면서까지 성능을 올릴 필요가 있는지는 모르겠다. 개인적인 생각으로는 이렇게까지 해서 성능을 올리고

[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를 붙이면 된다. 전체

[Scala] implicit keyword (4) - 마치며

지난 3번의 포스팅으로 Scala에서 implicit keyword의 3가지 사용법인 implicit converter , implicit class , implicit parameter 에 대해서 알아봤다. 이 3가지 모두 약간씩 다르지만, 명시적으로 적어야 하는 코드를 줄여 코드를 간편하게 만들어 주는 역할을 해준다. 이들은 적절하게 사용하면 verbose 코드를 줄여주어 가독성을 높여줄 뿐 아니라, Scala를 이용하여 domain-specific language (a.k.a. DSL)를 만들기 쉽게 만들어 준다. 실제로 많이 쓰이는 Play framework의 Configuration file 이나 route file 또는 Scala와 Java 양쪽에서 많이 쓰이는 SBT 는 Scala로 구현한 일종의 DSL로 Scala를 이용하여 static하게 compile 가능하게 되어있다. 하지만 이런 기능은 잘못 사용하면 해가 된다. 뭐 어떤 기능이 안 그러겠는가만은 특히 DSL을 만들기 쉽게 해준다는 것은 결국 기존의 언어가 아닌 새로운 언어를 정의하기 쉽게 된다는 것이고, 너무 많이 사용된 라이브러리는 Scala가 아닌 새로운 언어를 다시 배워야 한다는 문제가 생기기도 한다. 실제로 SBT가 Scala로 컴파일되는 DSL이지만 필자는 아직도 SBT의 문법을 완벽히 이해하지 못했을 정도로 완전히 새로운 언어가 되었다. 또한, 많은 사람이 Scala를 처음 사용하면서 어려워하는 부분이 implicit converter와 implicit class로 인해 현재 class에 정의되지 않은 함수가 호출되는 것이다. 1) Implicit keyword는 Scala의 난이도를 높여주는 장애물인 것은 분명하지만, 제대로 이해하고 있으면 새로운 세상을 보여주는 받침대가 된다. 1) 이전 글 에서도 말했지만, 이러한 특성 때문에 Scala를 처음 사용할 때는 다른 언어보다 더욱더 IDE 가 필요하다. 다시 한 번 IntelliJ IDEA 를 추천한다.

[Scala] implicit keyword (3) - Implicit parameter

implicit keyword의 3번째 사용법은 implicit parameter와 implicit value를 선언하는 것이다. implicit parameter는 함수를 호출할 때 인자를 생략할 수 있도록 해준다. 정확히는 함수를 호출할 때 인자를 생략하면 자동으로 그 인자를 채워서 실행시켜준다. 이때 무엇으로 채워주는지를 정하는 규칙은 2가지가 있다. 1) 첫 번째 규칙은 함수가 호출된 scope에서 prefix 없이 접근할 수 있는 implicit parameter와 같은 타입의 변수 중에 implicit label이 붙은 변수를 사용하는 것이다. 이 규칙에는 2가지 타입의 변수가 해당한다. 하나는 implicit parameter이고, 다른 하나는 해당 스코프에 선언된 implicit modifier가 붙은 local 변수이다. 이때 함수가 호출된 스코프에서 해당하는 규칙에 적용되는 변수가 2개 이상 있으면 "ambiguous implicit values" 라는 메시지와 함께 컴파일 에러를 발생시킨다. 여기서 주의해야 할 점은 반드시 implicit parameter와 같은 타입의 변수여야 한다는 것이다. 설령 implicit converter 로 변환 가능한 변수가 있어도 이는 implicit parameter로 넘어가지 않는다. 두 번째 규칙은 companion object에 정의된 변수 중 implicit parameter와 같은 타입으로 선언된 implicit label이 붙은 변수를 사용하는 것이다. 이는 첫 번째 규칙에 해당하는 변수가 없을 때 사용되고, 첫 번째 규칙과 마찬가지로 두 번째 규칙에 해당하는 변수가 2개 이상 있으면 "ambiguous implicit values" 라는 메시지와 함께 컴파일 에러를 발생시킨다. implicit converter를 적용하지 않는 것도 첫 번째 규칙과 같다. Scala library에서 implicit parameter를 잘 활용하는 대표적인 예는 F

[Scala] implicit keyword (2) - Implicit class

Scala에서 implicit keyword를 사용하는 또 다른 예제는 Implicit class 이다. Implicit class는 이미 존재하는 class에 새로운 함수를 추가하여 확장하기 위해서 사용된다. 정확히 말하면 원래 있던 클래스 자체를 바꾸지는 않고, class를 implicit conversion해서 호출할 수 있는 함수를 추가할 수 있게 해준다. Standard library에 있는 implicit class 의 대표적인 예는 Duration class들 이다. 위의 예제에서 보듯이 DurationInt 와 DurationDouble 이라는 implicit class 가 각각 Int 와 Double 을 확장시켜서 seconds / milliseconds / nanoseconds 같은 method를 추가해서 Duration 객체를 만들 수 있게 해준다. 이 설명을 보면 지난 글 에서 설명해줬던 implicit converter가 해줄 수 있는 일과 크게 다르지 않아 보인다. 사실 Implicit class는 implicit converter의 syntactic sugar 에 불과하다. 내부적으로 implicit class 로 정의된 class 는 class 의 이름과 같은 implicit converter를 같은 scope에 추가한다. 즉, 아래와 같이 정의된 implicit class 는 다음과 같이 변환된다. 내부적으로 implicit converter에 해당하는 함수를 정의하는 것이기 때문에 몇 가지 제약사항이 있다. 첫 번째 제약사항으로 implicit class 는 trait 이나 class 나 object 안에 정의되어야 한다. 이는 함수의 정의가 trait 이나 class 나 object 안에 정의되어야 하기 때문이다. 그래서 보통 implicit class들 은 package object 안에 정의된다. 두 번째 제약사항은 non-implicit인 parameter가 반드시 1개인 constru

[Scala] implicit keyword (1) - implicit converter

Implicit converter가 하는 일은 이름 그대로 value를 적절한 type으로 implicit하게 convert 하는 것이다. Scala는 기본적으로 strong typed language이기 때문에 implicit conversion을 지원하지 않는다. 함수를 호출할 때 함수의 symbol에 맞지 않으면 바로 컴파일에러가 발생한다. implicit conversion을 하기 위해서는 해당 scope 안에 implicit converter를 구현해두어야 한다. Implicit converter의 선언은 다음과 같다. Implicit converter는 unary function 에 implicit keyword를 붙여주는 것으로 정의된다. 이렇게 Implicit converter를 구현해두면, sizeToRectangle 함수가 정의되어 있는 scope에서는 Size 객체가 Rectangle 객체로 implicit conversion이 가능해진다. 예를 들어 Scala에서 자주 쓰이는 Option 이라는 class 를 보자. Option은 c#의 nullable 과 유사한 type으로 원소가 없을 수 있는 객체 이다. 이는 다른 관점에서 보면 최대 원소가 1개인 collection이라고 볼 수 있고, Option 을 사용할 때 collection처럼 list comprehensive method 1) 를 사용하는 것을 권장한다. 하지만 Option 코드 를 보면 Option 은 Iterable 이나 Traversable 을 상속받지 않았고, 일부 함수를 제외하고 collection처럼 이용는데 필요한 모든 함수를 가지고 있지도 않다. 대신 Option 을 Iterable 로 변환해주는 option2Iterable 이라는 implicit converter가 있기 때문에 Iterable 이 필요한 경우 자동으로 변환해서 넘겨준다. Implicit converter에 대응되는 개념이 다른 언어에 없는 것은 아니다. C++에서는 un

이 블로그의 인기 게시물

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

RAII는 무엇인가

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

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

[Web] SpeechSynthesis - TTS API