의견.png

스칼라 (프로그래밍)

위키원
ajm97 (토론 | 기여)님의 2019년 6월 28일 (금) 10:37 판 (쉬운 언어 확장과 DSL)
이동: 둘러보기, 검색

스칼라(Scala)는 2004년 마틴 오더스키(Martin Odersky)가 기존 자바 언어가 너무 복잡하다는 단점을 극복하기 위해 개발한 프로그래밍 언어이다. 스칼라(Scala)는 Scalable Language에서 따온 말이다. 간결한 소스 코드를 사용하여 자바(Java)에서 구현할 수 있는 대부분의 기능을 구현할 수 있다.[1]

리스지분증명 방식의 합의 알고리즘을 사용하는 암호화폐웨이브즈(Waves)는 스칼라 및 자바스크립트 언어로 개발되었다.

개요

스칼라는 다른 언어로 표현할 수 있는 패턴을 매우 간결하게 표현할 수 있는 범용 프래그래밍 언어이다. 객체 지향 프로그래밍 언어와 함수형 프로그래밍의 요소의 결합과 자바 API의 적용을 통해 프로그래머의 생산성을 높이는데 도움을 준다.[1] 그 예로 같은 기능을 하는 프로그램을 작성했을 대 자바로 작성했을 때에 비해 2~3배의 코드 길이가 줄어든다. 자바 런타임 환경(JRE)와의 상호 호환성이 좋으며, .NET을 위한 지원도 제공하고 있다. 트위터, 링크드인에서 활발하게 사용 중이지만, 메이저 언어의 자리는 아니다. 마이너 중에서도 메이저의 위치이다.[2]

주요 특징

스칼라는 마틴 오더스키가 자바 제네틱 컴파일러를 개발하며 느꼈던 자바의 단점들을 근본적으로 수정하고, 추후 프로그래밍 언어 연구를 위한 연구 플랫폼으로 함께 사용하기 위하여 디자인한 언어이다. 따라서 언뜻 보기에는 자바와 비슷해보여도 세부적, 다양한 측면에서 더욱 발전된 형태를 가지고 있다.[2]

쉬운 언어 확장과 DSL

언어 이름의 유래가 Scalable Launguage 의 약자로 괜히 스칼라라고 정해진 것이 아니다. 쉽게 언어를 확장하여 도메인 특화 언어(Domain Specific Launguage;DSL)을 만들 수 있으며, DSL 제작을 언어 차원에서 작정하고 밀어주고 있기 때문에 DSL 제작이 적은 소스로도 용이하며 DSL과 오리지널 스칼라를 섞어 쓰기도 쉬워 DSL 사용이 용이하다. DSL 정의의 단적인 예로는 BASIC 문법을 정의한 BAYSICK 이 있다.

  • 연산자
    • 스칼라에서는 자바에선 불가능 했던 식별자명을 식별자에 붙일 수 있다. 예를 들어 +라는 메소드를 정의하면 first. +(second)같은 식이 유효해진다. 여기에 더해 스칼라에선 a.f(b)를 a f b의 형태(객체)로 쓸 수 있기 때문에 first + second도 유효하다. 이런 식으로 자바에선 불가능했던 연산자 오버로딩을 할 수 있다.
  • 암묵
    • 암묵적으로 어떤 객체를 다른 타입으로 변환할 수 있다. 이것을 이용해서 위임을 편하게 만들어 원래 타입에 존재하지 않는 메소드를 애초에 있던 것처럼 호출할 수 있다. 예를 들어 자바의 BigInteger를 사용하려 하는데 .add()등을 사용하지 않고 암묵 변환을 이용해서 보조 클래스를 만들면 BigInteger에 +등의 연산자를 적용할 수도 있다. 또한 암묵적으로 메소드 인자에 쓰일 값을 받을 수 있다.
  • 이름으로 평가(call-by-name evaluation)
    • 인자로 함수를 넘기다 못해 아예 코드 블록을 넘길 수 있다. 사실 구현 자체는 단순히 인자 없는 익명 함수에서 인자를 받는 부분을 생략하는 것 뿐이지만, 호출 축에서는 코드를 그대로 넘겨서 호출할 수 있다. 즉 아에 처음부터 언어에 있었던 것 같은 기능을 만드는 것도 가능하다.

트레이드(trait)

클래스는 아닌데 상속을, 그것도 여러 번 할 수 있는 점에서 자바 인터페이스와 비슷할 수 있으나, 실제로 자바 인터페이스를 스칼라에서는 트레이트로 인식하고 있어, 그냥 트레이트를 자바 인터페이스처럼 사용할 수 있다. 하지만 트레이트는 자바 인터페이스와는 달리 구체적인 구현을 담고 있으며, 믹스인(클래스의 확장성이 높다는 것)이 가능하며, AOP를 언어 차원에서 지원한다. 또한 자바 인터페이스와 마찬가지로 구현하지 않은 메소드는 상속하는 쪽에서 구현해야 하므로 트레이트를 추상 클래스 급으로 만들어 놓고 믹스인으로 의존성을 주입할 수 있다. 이런식으로 인터페이스처럼 보이지만 실질적으로는 믹스인을할 수 있는 모듈이라고 생각하면 좋다. 드레이트를 모듈로 쓰게 되면 with로 트레이트를 쭉 쌓게 되는데, 여기서 착안해서 이런 방식을 '케이크 패턴'이라고 부른다.

짧은 소스

자바의 긴 코드였던 부분들이 짧고 간편하게 개선되어있다.

  • 생성자, 상속
    • 클래스의 생성자, 슈퍼클래스 상속, 슈퍼클래스 생성자 호출이 클래스 선언과 융합되었다. 보통 class 클래스명(인자1, 인자2, ...) extends 슈퍼클래스명(인자1, 인자2, ...){...}의 형태가 된다. 클래스명 옆의 인자 목록은 기본 클래스 생성자의 인자 목록이며, 슈퍼클래스명 옆의 인자 목록은 호출할 슈퍼클래스의 생성자에 해당하는 인자 목록이다. 이 외에 생성자를 오버로딩하고 싶다면 보조 생성자를 추가로 만들 수 있다. 자바와는 달리 보조 생성자의 이름은 무조건 this이다. 위 형태에서 기본 생성자의 본문이 어디있나 궁금할 수 있는데, '클래스 본문 전체가 기본 생성자의 본문이 된다'는 구성이다.
  • 게터/세터 자동 생성
    • 필드를 선언하면 그 필드에 따라 내부적으로 게터/세터가 생성된다. "객체.필드"이나 "객체.필드=값"으로 게터와 세터를 호출할 수 있다. 자동 생성된 게터와 세터는 명시적으로 오버라이드 가능하다. 단, 내부적으로 생성되는 게터/세터는 자바 스타일이 아니기 때문에 자바 쪽에서 호출하려면 @BeanProperty 어노테이션을 사용해야 한다.
  • 싱글톤
    • object 기워드로 싱글톤 오브젝트를 만들 수 있다. 또한 스칼라에는 자바의 static에 대응하는 키워드가 없고 클래스와 같은 이름의 오브젝트(컴패니언 오브젝트)를 만들 수 있다. 때문에 자바에서라면 한 클래스 안에 존재했을 정적 코드와 클래스 코드가 자연스럽게 분리된다. 싱글톤을 원클릭으로 만들 수 있는 것도 장점이지만 static을 일일이 붙이지 않아도 되는 것도 장점 중 하나이다.
  • 타입 추론
    • 변수의 타입. 함수의 반환값의 타입 등을 컴파일러가 추론해준다. 별 거 아닌 것 같아 보여도 코드 양이 줄어드는데 큰 기여를 한다. 자바에서는 personp=new Person();이라고 썼는데, 스칼라에서는 같은 타입 이야기를 두 번 하지 않아도 된다. 함수 반환값 추론은 사실 의식적으로는 거의 쓰이지 않고, 재귀에서는 추론이 불가능하며, 재귀가 아니더라도 클래스의 public 함수에서는 타입을 명시하는 것이 관행이기 때문에 함수 반환값 추론이 빛을 발할 때는 익명 함수를 사용할 때 뿐이다.
  • apply
    • apply는 C++에서 () 연산자를 오버로드하는 것을 떠올리면 된다. 예를 들어 배열을 임의 참조할 때 자바는 배열이 특수 객체로서 []을 사용할 수 있지만, 스칼라에서는 배열도 보통 객체 취급을 받으며 대신 apply가 구현되어 있어 ()로 임의 위치를 참조 가능하다. 또한 컴패니언 오브젝트에 apply를 구현해두면 생성자 대용으로도 쓸 수 있는데, 배열 등을 중첩할 때 new를 사용하지 않아도 되므로 편리하다. 심지어는 사실 함수 객체도 apply로 구현되어 있다.
  • 그 외 편의 문법
    • 익명 함수에 대한 편의 문법을 제공한다. 예를 들어 (a,b)=?a+b라는 익명 함수가 있다고 생각해보자. 이것만으로도 익명 클래스에 메소드를 구현하는 것에 비해 충분히 짧지만 , _+_라는 단 세글자로. 그것도 기독성이 매우 높게 축약하는 것이 가능하다.

패턴 매칭

자바의 switch-case문과 비슷한다. 겉으로 보이는 차이점은 fall-through가 없다는 정도이지만, 가드와 unapply 메소드와 연계되어 있기 때문의 switch문보다 복잡하게 실행되고, unapply 메소드가 핵심이라고 할 수 있다.

  • unapply
    • unapply는 이름처럼 apply의 반대인데, apply처럼 괄호 안에 목록을 집어넣으면 그 안에 값을 집어넣는다. 즉 디스트럭처링(destructuring)을 손쉽게하게 해준다. 예를 들어, 조금 특수한 경우를 생각해보면 ("hash","코인위키")라는 순서쌍이 t라는 이름으로 선언되어 있을 때. (key, val)=t라고 써넣으면 key와 val에 각각 "hash"와 "코인위키"가 들어간다. 이 예제처럼 단독으로 쓰일 수도 있지만, 보통은 패턴 매칭에서 구조를 분해하는 데 사용한다.

함수형 프로그래밍 언어

함수형 프로그래밍을 잘 지원하도록 설계가 되어진 언어이다.

  • 함수는 값이다
    • 함수형 언어의 기본 특징인데, 함수도 일반적으로 '데이터'라고 생각하는 Int나 String 타입의 값들과 동일한 취급을 받는다. 또한, 스칼라에서 모든 값은 객체이므로 함수도 객체이다. 스칼라에는 인자의 갯수에 따라 Function0부터 Function22 까지의 클래스가 기본으로 정의되어 있다. 따라서 함수도 다른 값처럼 변수에 담기고, 인자로 넘어가고, 값으로 반환될 수 있다. 예를 들어 함수 리터럴도 지원된다. 예를 들어 val increment = (i: Int) => i + 1 라는 식이 있다고 치자, 1이 Int 타입의 리터럴이고 "안녕 세계!"가 String 타입의 리터럴이듯이, (i: Int) => i + 1 도 Function1[Int, Int]타입의 리터럴이다. 즉 최근에 자주 언급되는 람다식(익명 함수)라고 생각하면 된다.
  • 불변성
    • 스칼라를 배우는 사람들에게 가장 먼저 가르치는 변수 선언의 기본 방식은 val로 가르치낟. val로 선언된 변수는 그 변수가 더이상 유효하지 않게 될 때 까지 그 값을 바꾸는 것이 불가능하다. 명령형 언어에서 자주 보이는 제 대입이 가능한 변수는 var로 선언해야 한다. 하지만 변수가 한 번 선언된 이후에 다른 객체를 가리키지 못하게 하는 것만으로는 충분한 불변성을 제공해주지는 않는다. val로 선언된 변수는 다른 객체를 가리킬 수는 없지만, 가리키고 있는 객체의 상태가 바뀌는 것은 막지 못한다.

예를 들어,

val arr = Array(1, 2, 3, 4, 5)
arr = Array(1, 2, 3) //다른 객체를 가리키는 것은 금지

은 불가능하지만

val arr = Array(1, 2, 3 ,4, 5)
arr(0) = 0 //가리키고 있는 객체의 상태가 바뀌는 것은 허용

은 가능하다.

이러한 결점을 보완하기 위해, 스칼라의 내장 콜렉션은 mutable(가변)과 imutable(불변)으로 나뉘어져 있고, 불변 콜렉션에는 원소에 대한 재대입이 불가능하다. 따라서 변경을 위해서는 변경된 새로운 콜렉션을 리턴하는 방식을 사용해야 한다. 위에서 든 예시인 Array는 가변 콜렉션이지만, 기본적으로는 임포트되는 스칼라의 고유 콜렉션들(List나 Vector 등)은 불변이 기본값이다. 따라서 스칼라에서는 클래스 안에서도 되도록이면 var를 쓰지 않도록 권장하고있다.

  • for comperhension
    • 스칼라의 for - yield 문은 Haskell의 do 와 같은 기능을 가진 것으로, 루프와는 전혀 다른 성질의 것이다(스칼라를 시작할 때에는 거부감을 적게 하기 위해 for을 가지고 루프하는 것부터 배우긴 한다). for - yield 문은 모나드를 위한 문법으로, 중첩된 flatMap과 map, 그리고 filter (정확히 말하면 withFilter) 메소드 호출을 깔끔하게 해주는 역할을 한다. 모나드를 많이 사용하다 보면 언어 내부의 일종의 미니 언어라고 생각하고 작성하게 되는 경우가 많다.
  • 타입클래스
    • OOP의 서브타이핑이 아니라, 타입클래스를 통한 다형성을 자연스럽게 지원하는 몇 안 되는 정적타입 언어 중 하나이다. 이 중에서도 서브타이핑과 타입클래스가 동시에 자연스럽게 지원되는 언어는 스칼라가 유일하다(스칼라가 하이브리드라고 불리는 이유 중 큰 부분을 차지한다). 서브타이핑과 타입클래스가 동시에 지원되는 바람에 생긴 문제가 없는 것은 아니다. class나 trait이나 extends와 같은 것들이 언어 차원에서 키워드로 지원되는 것과 달리, 스칼라에서의 타입클래스는 하나의 패턴으로서 다른 언어의 기능들을 활용해서 지원되는 것이고, 언어 차원의 키워드가 제공되지 않는다. 따라서 라이브러리에 따라 조금씩 타입클래스를 작성하는 방법이 살짝씩 달라 호환되지 않는 경우가 발생할 수도 있다. 하지만 모든 분야마다 실질적인 표준으로 자리매김한 라이브러리들이 존재하고 대부분의 라이브러리들이 타입클래스의 작성 방식을 한 가지 방식으로 통일한 현 시점에서는 호환성 문제는 크게 걱정하지 않아도 된다.
  • 함수형 프로그래밍 생태계
    • 스칼라의 모기업이라고 할 수 있는 Lightbend의 도구들이(Play, Akka 등) 엔터프라이즈 영역과 자바와의 호환성을 타겟으로 한다면 Typelevel 프로젝트의 도구들은 함수형 프로그래밍을 포함해, 스칼라 컴파일러의 능력을 극한으로 끌어낸 타입 안정성에 초점을 둔다. 따라서 함수형 프로그래밍을 하게 되면 scalaz와 cats라이브러리를 자주 보게 된다.
  • 스칼라에서의 함수형 프로그래밍에 대한 한계
    • JVMTCO를 미지원하기 때문에 많은 경우에 트램폴린을 통해 안정성을 확보하지만, 코드가 복잡해지고, 스택이 아니라 힙을 사용하는 이상 캐시미스가 필연적이라 성능이 하락한다
    • 서브타이핑 지원으로 인해 전역 타입추론이 불가능하고, 지역 타입추론만이 가능하다.
    • 타입클래스 인코딩에 대한 언어 자체적인 내장 기능이 없다.
    • 암묵적인 요소를 많이 사용하면 컴파일 시간이 늘어난다.
    • Haskell보다 타수가 많다.
    • Haskell 등이 지원하는 몇몇 고급 기능을 지원하지 않는다. Kind polymorphism 등.
    • 함수들이 기본적으로 커리잉(Currying)이 된 상태가 아니다.

자바와의 상위 호환

스칼라에서는 별다른 절차 없이 자바 코드를 100% 가져다 쓸 수 있다. 원하는 자바 라이브러리가 있다면 sbt에서 불러오게 하면 된다. 하지만 자바에서는 스칼라 코드를 그대로 쓸 수 없다. 이유는 스칼라에서 정의한 연산자나 메소드 등을 자바에서는 스칼라에서 쓰던 식으로 사용할 수 없기 때문이다.

XML 지원

XML 리터럴을 지원한다. 즉 XML 문서를 만들어내고 싶으면 스칼라 소스 내에 XML을 하드코딩하는 행위가 가능하다는 것이다. 또한 XML 리터럴 내에서스칼라 코드를 집어넣어 동적으로 XML을 생성할 수 있고, XPath 탐색을 지원해서 XML 파싱이 쉽다. 하지만 현재 Semi-strucured Document 를 위한 사실상의 표준은 JSON 이며, XML에 대한 열기가 식은 지금 스칼라 3(Dotty)에서는 지원을 중단할 예정이다.

기타

컴파일 언어이며 인터프리터가 따로 있는 것도 아닌데 REPL을 지원한다. 그 이유로는 입력->컴파일->실행->출력의 과정을 거치기 때문에 가능하다.[2]

자바와의 차이점

타입추론

스칼라는 정적타이핑이지만 타입추론을 제공하고 있기 때문에 타입에 대해서 많은 정보를 명시하지 않아도 된다.[3]

자료형

자바에의 자료형은 기본 자료형(int, short, long, float, double, byte, char, boolean)과 참조 자료형(기본 자료형의 조합으로 생성한 클래스)으로 나뉜다. 이는 성능에는 도움이 될 지 몰라도 기본 자료형과 참조 자료형 간의 변환 문제로 언어의 표현이 복잡해지는 경향이 잇다. 이에 반해 스칼라에서는 스몰토크나 루비와 같이 모든 자료형을 객체로 취급하고 있다. 또한 스칼라의 모든 객체는 scala 패키지의 any를 최상위 클래스로 값(AnyVal)과 레퍼런스(AnyRef)를 모두 아우르고 있다.[1]

싱글톤 객체

자바에서는 생성자를 private를 통해 선언하고 메소드를 static으로의 선언을 통해 싱글턴 객체를 생성한다. 이렇게 생성된 객체는 생성자가 private으로 선어되어있기 때문에 새로 객체를 생성할 수 없고 static 메소드를 통해 어디서나 접근하는 것이 가능하다. 하지만 static은 객체지향이 지향하는 바에 부합하지도 않을 뿐 더러 객체를 프로그램 실행 초기에 미리 생성해두어야 하기 때문에 자원의 낭비 가능성이 존재하게 된다. 이를 스칼라에서든 오브젝트라는 키워드를 통해 선언한다. 내부동작에는 크게 차이가 없으나 코드 표현이 있어서 간결성을 보인다.[1]

다중 상속

자바에서는 인터페이스를 통해 인터페이스 내에 정의만 기능하고 구현은 인터페이스를 상속받은 클래스 내에서 구현을 해야 하지만, 스칼라에서는 트레이트(trait)를 통해 정의만 하는것 뿐만이 아니라 실제 구현도 가능하기 때문에 사용자가 직접 구현을 하지 않아도 되는 장점을 가지고 있다.[4]

각주

  1. 1.0 1.1 1.2 1.3 스칼라〉, 《위키백과》
  2. 2.0 2.1 2.2 스칼라〉《나무위키》
  3. Outsider, 〈스칼라 타입에 대해서〉, 《볼로그 아웃사이더》, 2010-06-01
  4. Ju Factory, 〈스칼라 트레이트〉, 《티스토리》, 2015-04-27

참고자료

같이 보기


  의견.png 이 스칼라 (프로그래밍) 문서는 프로그래밍에 관한 토막글입니다. 위키 문서는 누구든지 자유롭게 편집할 수 있습니다. [편집]을 눌러 이 문서의 내용을 채워주세요.