스칼라 (프로그래밍 언어)

스칼라()는 객체 지향 프로그래밍 언어와 함수형 프로그래밍의 요소가 결합된 다중패러다임 프로그래밍 언어이다. 스칼라라는 이름은 "Scalable Language (확장 가능한 언어)"에서 유래되었다.

Java와 다르게, 스칼라는 커링, 불변성, 느긋한 계산법, 패턴 매칭 등 여러 함수형 프로그래밍 언어의 기능을 가지고 있다. 스칼라의 자료형 체계는 대수적 자료형, 공변성, 고차 자료형, 익명 자료형을 지원해 Java에서는 이룰 수 없는 높은 수준의 추상화를 달성할 수 있다.

스칼라는 2004년 마틴 오더스키가 처음 개발하여 배포했다. 간결한 소스 코드를 사용하여 Java에서 구현할 수 있는 대부분의 기능을 구현할 수 있다. Scala는 자바 바이트코드를 사용하기 때문에 자바 가상 머신(JVM)에서 실행할 수 있고, Java 언어와 호환되어 대부분의 자바 API를 그대로 사용할 수 있다.

자바와의 연관성

공통점 
대부분의 스칼라 관련 문서들에서 스칼라와 자바의 연관성을 '너무 바빠서 다른 언어를 따로 배울 시간이 없는 자바 프로그래머를 위한'이라는 표현을 사용하여 나타낼 정도로 비슷한 부분이 많이 나타난다.

패키지 
import 키워드를 통해 사용할 패키지의 선언을 한다. 이 때 _(밑줄, underscore)을 사용함을 통해 패키지나 객체의 멤버에 대해 접근성을 향상시킬 수 있다.
import java.util.Scanner
import scala.collection.mutable._

자바 API의 사용 
스칼라에서는 자바 API도 사용할 수 있다. 다음은 java.util.Scanner를 통해 문자열 입력을 받아 출력하는 예제이다.
import java.util._

object Example1 {
  val s = new Scanner(System.in)
  def main(args: Array[String]): Unit = {
    println("Your input: " + s.nextLine)
  }
}

실행 환경 
스칼라 고유의 라이브러리 파일을 추가로 가지고 있으면 기존의 자바 가상 머신에서 그대로 실행할 수 있다. 다음은 리눅스에서 자바 실행기(CUI환경)를 통해 스칼라로 컴파일한 프로그램을 실행하는 예이다.(bin에 클래스 파일들이, lib에 스칼라 라이브러리 파일들이 있다고 가정한다.)
java -cp .:./bin:./lib/* Example1

차이점

타입 추론 
컴파일러에 의해 타입 추론이 가능한 경우, 변수의 타입, 함수의 리턴 타입 등을 생략할 수 있다.

예: val x = "foo" , var x = 1.5

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

싱글턴 객체 

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

다중 상속 

자바와 다르게, 스칼라는 트레잇(Trait)을 이용해 다중상속을 구현할 수 있다. 스칼라의 트레잇은 자바의 인터페이스와 비슷하지만, 구체적인 구현을 담을 수 있다는 점에서 큰 차이점을 가진다. with 키워드를 이용해 트레잇을 여러개 상속한다면 트레잇은 상속한 순서에 따라 믹스인되어 상속의 모호성 문제("다이아몬드 문제")를 피할 수 있다.

스칼라 API 
 각 버전별 Scala API 문서
 scaladoc
 scaladoc nightly

자바 API

예제

Hello, world 프로그램 
object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello, world!")
  }
}

Hello, world 프로그램 2 
object HelloWorld extends App {
  println "Hello, world!"
}

퀵 정렬 프로그램 

명령형 프로그래밍 방식으로 구현한 경우 ▼
def qsort(arr: Array[Int]): Array[Int] = {
  def swap(i: Int, j: Int): Unit = {
    val tmp = arr(i)
    arr(i) = arr(j)
    arr(j) = tmp
  }

  def partition(left: Int, right: Int): Unit =
    if (right <= left ) ()
    else {
      val pivot = arr((left + right) / 2)
      var i = left
      var j = right

      while (i <= j) {
        while (arr(i) < pivot) i += 1
        while (arr(j) > pivot) j -= 1

        if (i <= j) {
          swap(i, j)

          i += 1
          j -= 1
        }
      }

    if (left < j) partition(left, j)
    if (j < right) partition(i, right)
  }

  partition(0, arr.length - 1)
  arr
}
if-else문을 사용한 경우 ▼
def qsort(seq: Seq[Int]): Seq[Int] =
  if (seq.isEmpty) seq
  else {
    val (pivot, rest) = (seq.head, seq.tail)
    val (lt, ge) = rest partition { _ < pivot }
    qsort(lt) ++ (pivot +: qsort(ge))
  }
패턴 매칭을 활용한 경우 ▼
def qsort(seq: Seq[Int]): Seq[Int] = seq match {
  case pivot +: rest =>
    val (lt, ge) = rest partition { _ < pivot }
    qsort(lt) ++ (pivot +: qsort(ge))
  case _             =>
    seq
}
제네릭을 활용한 경우 ▼
def qsort[T](seq: Seq[T], lessThan: (T, T) => Boolean): Seq[T] = seq match {
  case pivot +: rest =>
    val (lt, ge) = rest partition { lessThan(_, pivot) }
    qsort(lt, lessThan) ++ (pivot +: qsort(ge, lessThan))
  case _             =>
    seq
}
뷰 바운드(View Bounds)을 활용한 경우 ▼
def qsort[T <% Ordered[T]](seq: Seq[T]): Seq[T] = seq match {
  case pivot +: rest =>
    val (lt, ge) = rest partition { _ < pivot }
    qsort(lt) ++ (pivot +: qsort(ge))
  case _             =>
    seq
}
컨텍스트 바운드(Context Bounds)을 활용한 경우 ▼
def qsort[T: Ordering](seq: Seq[T]): Seq[T] = seq match {
  case pivot +: rest =>
    val (lt, ge) =
      rest partition { implicitly[Ordering[T]].lt(_, pivot) }
    qsort(lt) ++ (pivot +: qsort(ge))
  case _             =>
    seq
}

각주

외부 링크 
 

객체 지향 프로그래밍 언어
함수형 프로그래밍 언어
명령형 프로그래밍 언어
JVM 프로그래밍 언어
2003년 개발된 프로그래밍 언어
자바 프로그래밍 언어 계열
스크립트 언어
스칼라로 작성된 자유 소프트웨어
BSD 라이선스 소프트웨어
자유 컴파일러와 인터프리터
패턴 매칭 프로그래밍 언어
소스 대 소스 컴파일러
병행 프로그래밍 언어
정적 타입 프로그래밍 언어