Stop Thinking, Just Do!

Sungsoo Kim's Blog

Why Scala?

tagsTags

2 June 2015


Article Source


Why Scala?

Scala가 어떤 언어인지 간단히 특징들을 살펴 보았지만, 이것만으로 왜 Scala를 써야하는지는 충분치 않을 것입니다. 어떤 특징들이 많은 개발자및 소프트웨어 회사들이 Scala를 선택하게 했는지 설명과 코드를 통해 좀 더 살펴 보도록 하겠습니다.

Scala is concise

짧은 코드가 항상 간결한 것은 아니지만, 간결한 코드는 짧습니다. 가끔 Scala에 대해 짧게 설명할 필요가 있는 경우,  이해를 돕기 위해 아래와 같은 비유를 들곤 합니다.

“자바로 쓰여진 코드는 대서사시 같지만, Scala는 시와 같다”

간결함과 그를 통한 강력한 표현력은 Scala의 가장 큰 언어적 장점입니다.

// this is Java

class IndexedString {

    private int index;

    private String string;

    public IndexedString(int index, String string) {

        this.index = index;

        this.string = string;

    }

}
// this is scala

class IndexedString(index: Int, string: String)

위의 예제는 int 형값인 index 와 string 값을 private 멤버로 가지는 IndexedString 이라는 동일한 클래스를 정의하는 자바와 Scala 코드입니다.

* 세미콜론(;)은 생략 가능합니다.

자바 코드와 Scala 코드를 비교해보면, 기존에 아무렇지도 않게 열심히 반복하며 작성하던 자바 코드가 얼마나 많은 중복을 가지고 있는지 알 수 있습니다. 클래스 이름은 생성자에서 반복되며 멤버변수 역시 이에 값을 할당해주기 위한 생성자의 지역변수로 반복되고 있습니다. 여긱다가 getter 와 setter 함수를 추가하고자 한다면 얼마나 많은 불필요한 반복이 발생할지 쉽게 예측 가능할 것입니다.

뒤에서 다시 언급하겠지만, Scala의 타입 선언 역시 간결한 방식을 택하고 있습니다. 자바의 타입 표현과 달리 Scala는 타입 추론(type inference) 기능을 가지고 있어 타입 선언시에도 불필요한 반복을 할 필요가 전혀 없습니다. 간단히, “너도 알고 나도 아는 것” 은 언제나 생략 가능합니다.

Scala is high-level

프로그램의 복잡도를 낮추기 위해서, 추상화를 통한 개념적인 코딩은 개발자에게 상당히 중요한 부분입니다.

문자열의 경우, 말 그대로 문자의 열이기 때문에 String class 는 Character 의 List 로 바라볼 수 도 있어야 합니다. 하지만 아래 예에서 보이듯이 자바에서는 문자열을 다룰때 문자열 그 자체로만 바라보고 절차적으로 코딩을 해야합니다.

//this is java

boolean hasUpperCase = false;

for (int i = 0; i \< string.length(); ++i) {

    if (Character.isUpperCase(string.charAt(i))) {

        hasUppperCase = true;

        break;

    }

}

문자열을 문자열 그대로 문자 하나씩 방문하며 바깥쪽의 변수에 결과를 직접 저장하며 (side-effect) 수행 후 종료하는 로직까지 직접 작성해야합니다.

이에 반해 Scala에서는, 문자열을 좀 더 고수준으로 character의 sequence 로 바라 볼 수 있으며 어떤 캐스팅이나 명시적 변환 없이 collection library에 존재하는 함수를 그대로 사용가능합니다.

val hasUpperCase = string.exists(\_.isUpper)

위의 예제에서 보듯이, string 이라는 문자열을 collection 으로 바라보고 collection library에 정의된 exists 함수에 _.isUpper 라는 명제(predicate) 를 전달해 대문자가 존재하는지 여부를 체크할 수 있습니다. String에는 exists 함수가 존재하지 않음에도 위 코드는 잘 컴파일이 되어 실행되며 각 문자열의 isUpper 라는 함수를 호출하여 true/false 여부를 체크합니다.

간단히 설명하자면, String을 Character의 Sequence로 바라 볼수 있다는 rule 이 정의 되어 있으며 컴파일 타임에 이 규칙을 기반으로 컴파일러가 추론을 하여 적절한 타입으로 변환하는 것 입니다.(정확히는, StringOps라는 wrapper class로 둘러싸여집니다.)  좀더 자세한 설명은 함수 literal 표현과 implicit conversion 에서 설명하도록 하겠습니다.

Scala is statically typed

최근 인기 있는 언어들의 특징 중 하나는 대부분의 언어가 dynamic typed 라는 점입니다. 그럼에도 불구하고 가장 최신 언어중 하나인 Scala는 정적 타입 언어이며, 자바와 같이 모든 타입은 명시되어야 하며, 컴파일 타임에 모든 타입 에러는 검증됩니다. 이에 더 나아가 Scala는 지금까지 언어중 가장 강력한 타입 제한을 하며, 여러 표현법을 통해 타입에 대해 좀 더 자세히 명시할 수  있습니다.

물론 정적 타입이 언어의 장점이라는 부분은 상당한 논쟁을 불러 일으킬 수 있는 부분이지만, 적어도 정적 언어가 refactoring 면이나, 함수의 인자나 객체의 property 등 에 대한 unit test는 필요 없는 점, 때로는 타입 자체가 문서화의 기능도 하고 있다는 점 등은 정적 타입언어의 강점이라고 할 수 있습니다.

기본적인 정적 타입 시스템외에 Scala가 가지고 있는 가장 큰 강점은 바로 타입 추론 입니다. 이를 통해 코드를 좀 더 간결하고 명료하게 만들 수 있습니다.

(때로는 타입 문서화나 컴파일 타임 검증을 위해 일부로 명시하는 경우도 있습니다.)

Type inference

Scala에서 모든 타입은 변수나 함수 뒤에  “:” 을  통해 명시하게 됩니다. 자바와 달리 뒤쪽에서 타입을 명시하는 데 이런 방식을 택하는 많은 언어들의 특징은 타입이 생략 가능하다는 점 입니다. Scala 역시 타입을 생략 할 수 있지만, 모든 생략된 타입은 컴파일 타임에 컴파일러에 의해 추론되어 바이트코드로 컴파일 되게 됩니다.

// generic 타입 생략

val x:Map[Int, String] = new HashMap()

//or

val x = new HashMap[Int, String]

val str = aaaa

//리턴 타입 생략

def hasUpperCase(str: String) = str.exists(\_.isUpper)

위의 예제처럼 간단한 경우부터 복잡한 함수 내부의 구현에서도 추론 가능하다면 대부분 생략 가능합니다.


comments powered by Disqus