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

스칼라
Scala
패러다임프로그래밍 패러다임: 함수형 프로그래밍, 객체 지향 프로그래밍, 명령형 프로그래밍, 병행 컴퓨팅
설계자마틴 오더스키
개발자École Polytechnique Fédérale de Lausanne
발표일2004년 1월 20일(2004-01-20)
최근 버전3.4.0 위키데이터에서 편집하기
최근 버전 출시일2024년 2월 29일(2024년 2월 29일)
미리보기 버전3.0.0-RC3 위키데이터에서 편집하기
미리보기 버전 출시일2021년 4월 19일(2021년 4월 19일)
자료형 체계정적 타입, 강한 타입, 추론적 타입, 구조적 타입
라이선스BSD 라이선스
파일 확장자.scala, .sc
웹사이트www.scala-lang.org
영향을 받은 언어
에펠, 얼랭, 하스켈, 자바, 리스프, 피자, 스텔데드 ML, OCaml, 스킴, 스몰토크, 오즈
영향을 준 언어
실론, 팬텀, F#, 코틀린, 랩소, 레드

스칼라(영어: Scala)는 객체 지향 프로그래밍 언어함수형 프로그래밍의 요소가 결합된 다중패러다임 프로그래밍 언어이다. 스칼라라는 이름은 "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): 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

자바 API

예제

Hello, world 프로그램

object HelloWorld {
  def main(args: Array): Unit = {
    println("Hello, world!")
  }
}

Hello, world 프로그램 2

object HelloWorld extends App {
  println "Hello, world!"
}

퀵 정렬 프로그램

명령형 프로그래밍 방식으로 구현한 경우 ▼

def qsort(arr: Array): Array = {
  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): Seq =
  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): Seq = seq match {
  case pivot +: rest =>
    val (lt, ge) = rest partition { _ < pivot }
    qsort(lt) ++ (pivot +: qsort(ge))
  case _             =>
    seq
}

제네릭을 활용한 경우 ▼

def qsort(seq: Seq, lessThan: (T, T) => Boolean): Seq = 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](seq: Seq): Seq = seq match {
  case pivot +: rest =>
    val (lt, ge) = rest partition { _ < pivot }
    qsort(lt) ++ (pivot +: qsort(ge))
  case _             =>
    seq
}

컨텍스트 바운드(Context Bounds)을 활용한 경우 ▼

def qsort(seq: Seq): Seq = seq match {
  case pivot +: rest =>
    val (lt, ge) =
      rest partition { implicitly].lt(_, pivot) }
    qsort(lt) ++ (pivot +: qsort(ge))
  case _             =>
    seq
}

각주

  1. “Scala 3.4.0”. 
  2. https://github.com/lampepfl/dotty/releases/tag/3.0.0-RC3.
  3. Programming in Scala, Martin Odersky, Lex Spoon, Bill Venners. Artima Press
  4. http://www.ibm.com/developerworks/java/library/j-scala01228.html - The busy Java developer's guide to Scala
  5. 서광열의 프로그래밍 언어 이야기, 마이크로소프트웨어 2008년 5월호
  6. Programming in Scala, Martin Odersky, Lex Spoon, Bill Venners. Artima Press
  7. http://kyungseo.pe.kr/blog/111 Archived 2010년 4월 29일 - 웨이백 머신 - 결론>정적 클래스 변수(메서드) vs. 싱글턴 패턴

외부 링크