Search Results for 'Scala'

7 POSTS

  1. 2008.12.01 객체지향+함수형 프로그래밍 언어 Scala 1
  2. 2008.04.28 명시적 정적 타이핑(typing)의 종말 2
  3. 2008.04.25 Scala BOF in Tokyo April 30
  4. 2008.04.22 Scala 이상한 점 2
  5. 2008.04.19 Scala에서 Parital Application
  6. 2008.04.19 Scala
  7. 2008.03.27 Scala 4

마이크로소프트웨어 2008년 5월 기고글입니다.

스칼라(Scala)는 객체지향 언어이면서 동시에 함수 언어이다. 스칼라는 자바와 마찬가지로 자바가상머신(JVM) 위에서 실행되며 기존 자바 라이브러리를 그대로 사용할 수 있다. 반대로 자바로 작성된 프로그램에서 스칼라로 작성된 라이브러리를 사용할 수도 있다. 다른 JVML(JVM 언어)인 JRuby, Jython과 마찬가지로 스칼라는 자바의 한계를 극복하기 위해 출현했다. 특히, 이미 설계된 컴포넌트 단위의 자바 라이브러리들을 효율적으로 재사용하기 위해서는 함수 언어의 특징인 고차 함수, 패턴 매칭 등이 필요하다는 것이다. 이 글에서는 자바와의 차이점을 부각시켜 스칼라의 특징과 장단점을 살펴보려 한다.


왜 새로운 언어가 필요했을까?


스칼라 언어를 만든 사람은 GJ, Pizza 프로젝트 등으로 유명한 마틴 오더스키(Martin Odersky)다. 그가 주도한 GJ와 Pizza는 자바 언어에 인자 다형성(parametric polymorphism)을 추가한 리서치 프로젝트였고 이런 결과물은 이후 자바 5의 제네릭스가 되었다. 하지만 JVM을 변경하지 않고 컴파일러 기술만으로 구현한 제네릭스는 성공이라고 보기 힘들었다. 오더스키는 이런 접근법에 한계를 느끼고, 함수 언어의 장점을 적극 반영한 새로운 JVML을 만들었다. 이렇게 나온 결과물이 스칼라이다.

 

스칼라의 목표는 컴포넌트 소프트웨어를 잘 지원하기 위한 언어이다. 자바는 대규모 개발 프로젝트에 주로 사용되는 언어이고, 각종 프레임워크를 가져와서 조합해 사용하는 경우가 다른 언어보다 압도적으로 많다. 하지만 자바 언어 자체는 소형 디바이스를 지원하기 위한 오크(Oak)라는 프로그래밍 언어에서 출발했고, 웹에서도 소형 애플릿 작성을 주로 사용되었다. 이런 자바를 각종 프레임워크로 포장해 대형 프로젝트에 사용하기 시작하다보니 언어의 한계로 인해 여러 문제점을 겪게 되었다.

 

컴포넌트 지원 언어는 2가지 특징을 가져야 한다. 첫째, 규모 가변적(scalable)해야 한다. 바꿔 말해, 컴포넌트의 크기와 관계없이 항상 같은 방법으로 사용할 수 있어야 한다는 것이다. 또한, 언어에 복잡한 요소를 더하기 보다는 추상화(abstraction), 조합(composition), 분해(decomposition)에 초점을 맞춰야 함을 의미한다. 둘째, 이런 특징을 만족시키기 위해서 프로그래밍 언어는 객체지향적일 뿐만 아니라 함수형이어야 한다.


HelloWorld


스칼라를 본격적으로 공부하기에 앞서 스칼라의 기본적인 문법과 취향을 느낄 수 있도록 먼저 HelloWorld 프로그램을 작성해보자. 비교를 위해 자바 HelloWorld를 나란히 배치했다.


object HelloWorld {

     def main(args: Array[String]) {

         println("Hello, world!")

     }

}

Scala HelloWorld


class HelloWorld {

     public static void main(String[] args) {

         System.out.println("Hello World!");

     }

}

Java HelloWorld



일단 가장 큰 차이점으로 HelloWorld 클래스를 선언할 때 스칼라 프로그램은 object라는 키워드를 쓰고 있다. 스칼라는 class와 object를 구분하는데, object는 싱글톤(singleton)으로 클래스의 객체를 하나만 생성함을 의미한다. 스칼라 HelloWorld는 싱글톤이기 때문에 main 메서드를 선언할 때도 static이라는 키워드를 사용하지 않는다.

 

자바에서는 화면에 문자열을 출력하기 위해 System.out.println을 사용했는데, 스칼라는 간결하게 println이라고 사용할 수 있다. 자바는 문장의 끝에 세미콜론을 항상 붙여줘야 하는데, 스칼라는 세미콜론이 없다. 자바는 타입을 먼저 쓰고 변수를 선언하는데, 스칼라는 변수 : 타입 형태로 변수를 선언한다. 자바에서 String[]인 문자열 배열은 스칼라에서는 Array[String]으로 쓴다.

 


스칼라 인터프리터와 컴파일러


스칼라를 설치하고 스칼라 바이너리(scala)를 실행시키면 다음과 같이 인터프리터 모드로 동작한다. 인터프리터 모드에서는 입력되는 식을 계산해서 결과를 보여준다. 이후 예제에서 scala>가 나오면 인터프리터에 입력한 것으로 생각하면 된다.

 

C:\Users\Administrator>scala

Welcome to Scala version 2.7.0-final (Java HotSpot(TM) Client VM, Java 1.6.0_10-

beta).

Type in expressions to have them evaluated.

Type :help for more information.

 

scala> 3+5

res1: Int = 8

스칼라 인터프리터


스칼라 컴파일러(scalac)는 스칼라 코드를 자바 클래스파일(바이트코드)로 컴파일해준다. 사용법은 자바 컴파일러(javac)와 유사하다. 위 HelloWorld.scala라는 컴파일하려면 다음과 같이 명령을 내려주면 된다.


C:\Users\Administrator>scalac HelloWorld.scala

스칼라 컴파일러


컴파일된 스칼라 프로그램을 실행시키려면 scala를 이용하면 된다.

 

C:\Users\Administrator>scala HelloWorld

Hello, world!

스칼라 프로그램의 실행



순수 객체지향 언어


자바의 타입은 기본 타입(primitive type)과 레퍼런스 타입(reference type)으로 나뉘고, 이진(boolean), 정수(int), 부동소수점(float, double) 등 기본 타입은 객체가 아니다. 이런 방식은 성능 향상에는 큰 도움이 되지만, 기본 타입과 레퍼런스 타입 사이를 변환하는 박싱(boxing), 언박싱(unboxing) 등의 문제로 프로그래밍 언어가 복잡해지는 문제가 있다.

 

자바 5에는 기본 타입과 레퍼런스 타입을 필요에 따라 자동변환해주는 오토박싱, 오토언박싱 기능이 들어갔지만, 기본 타입과 레퍼런스 타입으로 이원화된 타입 시스템 자체는 달라지지 않았다.

 

반대로 스칼라는 스몰토크, 루비와 마찬가지로 순수 객체지향 언어이다. 스칼라는 정수 5가 scala.Int 클래스의 객체이며, +, -, , *, / 등의 연산자는 하나의 인자를 받는 메서드다. 예를 들어 1 + 2 * 3 / x 같은 수식은 1.+(2.*3./(x)))와 같이 전부 메서드 호출로 변경된다. 스칼라에서는 자바에서 특수 문자로 취급하던 +, -, *, / 등의 문자도 메서드 이름으로 사용할 수 있음을 의미한다.

 

아래 그림에서 볼 수 있듯이 스칼라의 클래스 계층도는 scala.Any를 최상위로 해서 값(scala.AnyVal)과 레퍼런스(scala.AnyRef)를 하나의 계층으로 아우르고 있다.


스칼라의 클래스 계층도



함수 언어


스칼라는 모든 함수를 객체로 취급한다. 바꿔 말해, 스칼라에서 함수를 함수의 인자로 넘길 수도 있고, 함수의 리턴 값으로 함수가 리턴될 수도 있음을 의미한다. 스칼라 입문서에 있는 간단한 예제를 하나 살펴보자.

 

object Timer {

     def oncePerSecond(callback: () => unit) {

         while (true) { callback(); Thread sleep 1000 }

     }

     def timeFlies() {

         println("time flies like an arrow...")

     }

     def main(args: Array[String]) {

         oncePerSecond(timeFlies)

     }

}

함수를 함수의 인자로 넘기는 예제



위 예제는 Timer라는 클래스를 만들어, main에서 oncePerSecond라는 메서드를 호출할 때 또 다른 메서드인 timeFlies를 인자로 넘겨준 예제이다. oncePerSecond 함수의 인자를 보면 callback이 () => unit이라는 타입을 가짐을 알 수 있는데, ()는 인자를 하나도 받지 않음을 뜻하고, unit은 자바의 void와 유사하게 리턴 값이 없음을 뜻한다. 위 프로그램은 1초마다 한번씩 callback으로 넘어온 timeFlies 메서드를 호출하게 된다.

 

이 예제에서 하나 재미있는 사실은 Thread의 sleep 메서드를 호출할 때 Thread sleep 1000라고 적은 부분이다. 스칼라에서는 인자가 하나인 메서드를 호출할 때, 메서드 호출자(.)를 생략하고 위와 같이 적을 수 있다. 앞서 +, - 등이 메서드라고 언급했는데 1.+(2)가 아닌 1 + 2로 적을 수 있는 이유도 마찬가지다. 1 + 2에서 1은 리시버 오브젝트(receiver object), +는 메서드, 2는 + 메서드의 인자인데, 스칼라에서는 간결함을 위해 1.+(2)라고 적는 대신에 1 + 2라고 적을 수 있게 허용한 것이다.



정적 타입 시스템


스칼라는 자바에 비해 간결한 문법을 제공하지만, 강력한 정적 타이핑을 제공한다. 스칼라는 동적 JVML 언어인 그루비, JRuby, Jython 등과 달리 컴파일 타임에 모든 타입 오류를 잡아낼 수 있다. 스칼라가 여러 컴포넌트를 통합하기 위해 만들어진 언어라는 점을 감안하면 통합 오류를 조기에 잡아내는 정적 타입 시스템은 자연스러운 선택이다.

 

대신 스칼라는 같은 정적 타이핑을 사용하는 자바와 달리 경우에 따라 타입을 생략할 수 있다. 사용자가 모든 타입을 적어주지 않더라도 스칼라 컴파일러가 타입 추론(type inference)을 통해 부족한 부분을 채워주기 때문이다. 타입 추론은 자바 스타일의 문법을 가진 스칼라를 동적 타이핑하는 그루비나 루비처럼 간결하게 만들어주는 핵심 요소이다. (스칼라 코드는 같은 일을 하는 자바 코드의 1/3 정도 밖에 안 된다.)


일례로, 정수 2개를 더해서 돌려주는 add 함수를 생각해보자. add 함수를 정의할 때 x와 y는 정수 타입으로 정의를 해줬지만 add 함수의 리턴 타입은 생략했다. 하지만 스칼라 컴파일러가 봤을 때 정수 x와 정수 y를 더한 값을 돌려주므로 리턴 타입은 자동으로 Int가 된다는 사실을 추론해낼 수 있다. 이 함수를 인터프리터에 입력해보면, 인터프리터가 add의 타입을 (Int,Int)Int라고 정확히 추론해냄을 볼 수 있다.

 

scala> def add(x: Int, y: Int) = x + y

add: (Int,Int)Int

스칼라 타입 추론 (리턴 타입을 추론한 경우)


물론 다음과 같이 타입을 모두 적어줘도 무방하다.


scala> def foo(x: Int, y: Int): Int = x + y

foo: (Int,Int)Int

스칼라 타입 추론 (모든 타입을 써준 경우)


하지만 동적 타이핑하는 언어와 달리 모든 타입을 생략하면 컴파일러가 타입을 추론할 없기 때문에 오류가 발생한다. 스칼라 코딩을 처음 시작한 사람들은 타입을 어느 정도 생략해도 되는지 알기 어려운데, 시행착오를 통해 컴파일러가 어디까지 타입 추론을 해주는지 감을 잡는 일이 필요하다. 단, 컴파일러가 추론할 수 있더라도 필요한 경우에는 적절히 타입을 써주면 코드의 가독성을 높일 수 있음을 명심하자.


scala> def foo(x, y) = x + y

<console>:1: error: ':' expected but ',' found.

     def foo(x, y) = x + y

                    ^

<console>:1: error: identifier expected but eof found.

     def foo(x, y) = x + y

                                 ^

스칼라 타입 추론 (실패)



선언(val/var/def)

 

스칼라는 변수를 선언할 때 자바처럼 모든 타입을 다 적어줄 필요가 없다. 변수 x를 선언하고 1을 넣어주려면 다음과 같이 var 키워드를 사용하면 된다. var은 자바 변수와 동일하다.


scala> var x = 1

x: Int = 1

scala> x = x + 1

x: Int = 2

var의 사용



스칼라는 var 외에도 val을 통해 값을 선언할 수 있는데, valvar로 선언된 변수와 달리 값이 변하지 않는다. val로 만든 x에 x + 1이라는 새로운 값을 집어넣으면 다음처럼 x는 변경 불가능한 값(immutable value)이라는 오류가 발생한다.

 

scala> val x = 1

x: Int = 1

 

scala> x = x + 1

<console>:7: error: assignment to immutable value

     x = x + 1

        ^

val의 사용


스칼라에는 var/val 외에도 함수 객체를 선언하는 데 사용하는 def 키워드가 있다. def는 다음처럼 val과 마찬가지로 변하지 않는 값을 선언하는 데 사용할 수 있다.


scala> def x = 1

x: Int

 

scala> x = x + 1

<console>:7: error: value x_= is not a member of object $iw

     x = x + 1

     ^

def의 사용


valdef의 차이는 연산을 하는 시점에 있다. valval을 선언하는 시점에 우변을 계산해서 값을 할당하는 반면에 def는 실제로 사용되는 시점마다 우변을 새로 계산한다. 다음 예제는 이 차이를 분명하게 보여준다.


scala> var x = 1

x: Int = 1

scala> val y = x + 1

y: Int = 2

scala> def z = x + 1

z: Int

scala> z

res13: Int = 2

scala> x = 2

x: Int = 2

scala> y

res15: Int = 2

scala> z

res16: Int = 3

var과 def의 차이


위 예제에서 var로 선언한 변수 x에 1을 넣은 다음에 y와 z 모두 x + 1로 정의해주었다. 다만 y는 val을 z는 def를 사용해서 선언하였다. 일단 val y는 선언하는 순간에 값이 2임을 표시해준 반면에 def z를 선언했을 때는 타입이 Int라는 사실만 알려주고 값을 계산하지 않았음을 알 수 있다. z를 실제로 사용했을 때 2로 계산해준다. 이후 변수 x의 값을 2로 변경했을 때, y는 이미 선언한 시점에서 계산이 끝났으므로 값이 변경되지 않고 여전히 2인 반면에 z는 다시 x + 1을 이 시점에서 새로 계산하기 때문에 x 값을 2로 계산해서 3을 돌려줌을 볼 수 있다.

 

클래스


스칼라의 클래스는 기본적으로 자바와 유사하다. 다만 별도의 생성자 없이 클래스 이름 옆에 객체 생성 시 어떤 인자를 받을 것인지 써준다는 차이가 있다. 아래 Person 클래스는 성과 이름을 입력받는 간단한 클래스이다.

 

class Person(fname: String, lname: String) {

     def firstname() = fname

     def lastname() = lname

 

     override def toString() = firstname() + " " + lastname()

}

스칼라 클래스의 예


toString 메서드는 java.lang.Object의 toString 메서드를 오버라이드(override)한 것이다. 스칼라는 상위 클래스의 메서드를 오버라이드할 때 명시적으로 override라는 키워드를 써줘야 한다. 실수로 상위 클래스의 메서드를 의도치 않게 오버라이드하는 것을 막기 위한 조치이다.


케이스 클래스(case class)와 패턴 매칭(pattern matching)

 

패턴 매칭은 함수 언어의 고유한 특징 중에 하나로 함수 언어를 강력하게 만들어주는 핵심이다. 스칼라는 케이스 클래스를 통해 객체지향 언어 속에 패턴 매칭을 녹여 넣었다.


abstract class Shape

case class Rectangle(width: Double, height: Double) extends Shape

case class Circle(radius: Double) extends Shape

case class Square(side: Double) extends Shape

케이스 클래스의 예


케이스 클래스는 일반 클래스와 달리 다음과 같은 몇 가지 특징을 갖는다.

 

(1) 새로운 객체를 생성하기 위해 new 키워드를 사용할 필요가 없다. new Circle(5.0) 대신에 Circle(5.0)이라고 사용할 수 있다.

(2) 생성자의 파라미터에 사용된 값을 얻을 수 있는 getter 메서드가 자동으로 생성된다. 예를 들어 var c = Circle(5.0)를 선언했다면 c.radius는 5.0을 리턴한다.

(3) equals와 hashCode 메서드가 자동으로 만들어진다.

(4) toString 메서드가 자동으로 정의된다. Rectangle(5.0, 3.0)을 출력해보면 "Rectangle(5.0, 3.0)"이 나온다.

(5) 패턴 매칭을 통해 분해(decompose)될 수 있다.

 

 

케이스 클래스의 최대 장점은 (5)에서 언급한 패턴 매칭의 사용이다. 다음은 패턴 매칭을 사용한 면적(area) 계산 함수이다.


def area(s: Shape): Double = s match {

     case Rectangle(width, height) => width * height

     case Circle(radius) => radius * radius * 3.14

     case Square(side) => side * side

     case _ => 0

}

 

def perimeter(s: Shape): Double = s match {

     case Rectangle(width, height) => 2 * (width + height)

     case Circle(radius) => 2 * 3.14 * radius

     case Square(side) => side * 4

     case _ => 0

}

패턴 매칭을 통한 Shape의 면적/둘레 계산


area(면적)와 perimeter(둘레) 함수는 Shape의 객체 s를 넘겨받아 패턴 매칭을 한다. s의 객체가 Rectangle, Circle, Square일 때 각각의 케이스에 대해 어떤 일을 수행할 것인지 적어주면 된다. 이때 각 객체를 만들 때 사용되었던 인자가 원하는 변수에 자동으로 매칭된다. 예를 들어, Rectangle(5.0, 3.0)을 s로 넘겼다면 첫 번째 케이스에서 width와 height는 각각 5.0, 3.0이 된다. _는 앞선 패턴 매칭이 모두 실패했을 때 디폴트로 호출되는 케이스를 의미한다.

 

객체지향 프로그래밍에 익숙한 개발자라면 area를 Shape의 메서드로 선언하고, Rectangle, Circle, Square 등 각각의 서브클래스가 area를 구현하는 방식으로 코드를 작성할 수도 있을 것이다. area나 perimeter 같은 메서드는 몇 개로 고정되어 있고, Triangle이나 Ellipse 등 새로운 Shape이 계속 추가되는 상황이라는 이 방법이 더 좋다. 새로 추가되는 Shape에서 area와 perimeter 등 몇 개의 메서드만 구현해주면 되고 기존 파일을 고칠 필요가 없기 때문이다.

 

반대로 Shape의 종류는 고정된 상황에서 area, perimeter 등의 메서드를 계속 추가해 나가야 되면 상황이 달라진다. 메서드가 하나 추가될 때마다 모든 Shape의 서브클래스를 찾아서 메서드를 추가해줘야 하기 때문이다. 자바를 비롯한 전통적인 객체지향 언어에서는 이 문제를 비지터 패턴(visitor pattern)을 이용한 더블 디스패치(double dispatch)로 풀었다. 하지만 비지터 패턴은 이해하기도 어렵고, 싱글 디스패치하는 일반적인 객체지향 언어에서 자연스러운 접근 방법도 아니다.

 

패턴 매칭은 이런 상황에서 완벽한 솔루션을 제공한다. 새로운 함수/메서드를 만들고 패턴 매칭을 통해 각각의 케이스를 다루면 되기 때문이다. 케이스 클래스라고 이름 붙인 이유는 이처럼 패턴 매칭을 통해 각 케이스를 다룬다는 면을 강조한 것이다.

 


제네리시티(Genericity)


마지막으로 살펴볼 내용은 자바 제네릭스에 대응하는 제네리시티이다. 스칼라의 제네리시티는 자바보다 문법적으로 훨씬 간결하고 직관적이다. 이해를 위해 스칼라 튜토리얼에 있는 예제 코드를 하나 살펴보자.


class Reference[a] {

     private var contents: a = _

     def set(value: a) { contents = value }

     def get: a = contents

}

제네리시티


아무 타입이나 저장할 수 있는 Reference 클래스를 만들기 위해서 [a]라는 타입 파라미터를 받았다. Reference의 contents 필드를 타입 a로 선언했고, get의 리턴 타입과 set의 value 타입을 a로 선언했다. 실제로 사용할 때는 val cell = new Reference[Int] 형태로 타입을 넘겨주면 된다. 참고로, 스칼라는 기본 타입과 레퍼런스 타입의 구분이 없기 때문에 Int를 넣기 위해 Integer 객체를 만들어 박싱할 필요가 없다.


정리

 

스칼라는 다른 함수 언어와 달리 JVM 위에서 동작하는 언어라는 이점 때문에 상당히 실용적이다. 특히 이미 기존 자바 개발자들에게 스칼라는 기존 프로젝트 플랫폼을 변경하지 않고 필요한 부분에서 생산성을 높일 수 있는 중요한 도구가 될 것이다.

 

스칼라 컴파일러와 런타임의 버전은 2.7.0으로 상당히 안정되어 있고, 지금은 스칼라를 기반으로 한 라이브러리와 프레임워크도 출연하기 시작했다. Lift는 스칼라를 이용한 웹 프레임워크이고, Scalax, Scalaz 프로젝트 등은 스칼라 라이브러리이다. 또한 멀티 코어 시대를 대비한 병렬 프로그래밍(얼랭(Erlang)과 유사한 프로세스 모델을 제공) 또한 스칼라가 자랑하는 기능 중 하나이다.

 

짧은 지면에 새로운 프로그래밍 언어를 처음부터 소개하려다보니 응용 부분에서 많은 내용이 빠졌다. 보다 자세한 내용은 스칼라 홈페이지(http://www.scala-lang.org/)를 통해 얻길 바란다.

 

 

Scalax, Scalaz

 

스칼라는 JDK에 있는 클래스들을 쉽게 사용할 수 있지만, 한편으로는 스칼라 프로그래밍 언어 자체의 특징을 살리기 위한 라이브러리의 개발도 한참이다. 대표적으로 Scalax와 Scalaz 프로젝트가 있다.

 

일례로 Scalaz는 ScalaCheck이라는 테스팅 프레임워크를 제공한다. 물론 스칼라도 자바 테스팅 프레임워크인 JUnit을 사용할 수 있지만, ScalaCheck은 JUnit과 달리 함수 언어의 특징을 살려 명세 기반의 자동화된 테스팅을 제공한다. 이는 헤스켈의 QuickCheck과 유사하다. 또한 Scalaz는 함수 언어의 특징을 살리고자 모나드(monad) 관련 패키지도 제공한다.

 

스칼라의 장점을 잘 살린 함수형 프로그래밍을 하고 싶다면 이런 라이브러리 프로젝트가 중요한 학습 소스가 될 것이다.



참고문헌


[1] A Scala Tutorial for Java Programmers

http://www.scala-lang.org/docu/files/ScalaTutorial.pdf

 

[2] Scala by Example

http://www.scala-lang.org/docu/files/ScalaByExample.pdf

 

[3] An Overview of the Scala Programming Language

http://www.scala-lang.org/docu/files/ScalaOverview.pdf

 

[4] The Scala Language Specification Version 2.7

http://www.scala-lang.org/docu/files/ScalaReference.pdf





































IBM dW의 Crossing borders: Typing strategies beyond the Java model는 프로그래밍 언어의 타이핑 전략(typing strategies)에 대해서 이야기하고 있습니다. 상당수 개발자들이 프로그래밍 언어의 타이핑 생산성, 유지보수성 등에 상당한 영향을 미친다고 생각하고 있고, 자바 개발자들이 상당수 루비로 갈아탄 것도 이와 무관하지 않아 보입니다.

이 기사의 저자는 자바 타이핑에 대한 대안으로 서로 양극단에 있는 Ruby와 OCaml의 타이핑을 비교하고 있습니다. Ruby는 동적 타이핑을 하는 대표적 언어이고, OCaml은 정적 타이핑의 대표 주자인 ML 계열의 언어입니다.

프로그래밍 언어의 타이핑을 구분하는 방법은 몇 가지가 있는데, 정적/동적 타이핑, 강한(strong)/약한(weak) 타이핑이 일반적인 구분 방법입니다. 여기에 하나 덧붙이자면 명시적/암묵적 타이핑이 있습니다. 명시적 타이핑 언어는 자바나 C#처럼 모든 타입을 개발자가 적어줘야 하는 방식이고, 암묵적 타이핑은 OCaml이나 Haskell처럼 개발자가 일부 타입을 기입하지 않더라도 타입 추론을 통해 컴파일러가 나머지 타입을 찾아내는 방식입니다.

자바가 루비에 비해 생산성이 떨어지는 문제를 동적/정적 타이핑의 문제로만 볼 수 없는 이유는 자바와 OCaml을 비교했을 때 자바 이상으로 강력한 정적 타이핑을 제공하는 OCaml 코드가 자바보다는 루비에 가까운 간결함을 보이기 때문입니다.

근데 하나 분명한 것은 자바는 아니라는 것입니다. 루비도 좋고 OCaml도 좋고 장단점이 있지만 명시적 정적 타이핑하는 자바는 이제 수명이 다하고 있다는 것이 핵심이 아닐까 싶습니다. Spring, AspectJ, Hibernate 등 다양한 자바 기반의 거대 프레임워크가 존재하는 이유는 반대로 생각하면 자바만으로는 일반적인 어플리케이션 개발에 요구되는 메타프로그래밍을 쉽게 할 수 없기 때문입니다.

물론 워낙 개발자가 많은 자바 진영이다 보니 그리 쉽게 망하겠냐는 생각을 하실 수도 있겠지만, 자바를 다른 언어로 대체하려는 움직임은 자바 진영 내부에서 가장 먼저 논의되고 있습니다. 요즘은 JVM에서 동작하는 언어(JVML)에 대한 이야기로 뜨겁습니다. 함수 언어와 객체지향 언어를 결합한 Scala가 신예라면, JVM 용 스크립트 언어인 Groovy, Jruby, Jython 등은 이미 많은 유저를 확보했습니다. 그 외에도, Lisp의 현대판인 Clojure, CLR과 JVM에서 동시에 동작하는 Fan, Scheme JVM 버전인 SISC 등이 그야말로 자바를 따라잡기 위해 노력하고 있는 상황입니다.

자바는 이런 언어의 라이브러리를 구현하기 위한 시스템 언어 정도로 물러서고 어플리케이션 프로그래밍은 자바가 아닌 좀 더 생산성이 높은 언어로 하는 시대가 올지도 모르겠습니다.


사족: 언급된 내용 중에서 제가 동적 타이핑에서 가장 불편하다고 생각하는 점 중에 하나는 IDE 개발 도구 지원입니다. Eclipse를 비롯한 자바 개발 도구는 대부분 자바 소스를 파싱해서 AST 형태로 만든 후 각종 리팩토링(이름 변경 등)을 매우 쉽게 만들어 줍니다. 반대로 Python이나 Ruby IDE는 어떤 모듈이나 클래스 인스턴스의 메소드 자동 완성하기가 힘듭니다.


Scala BOF in Tokyo April 30

Posted 2008. 4. 25. 08:51
일본 자바 유저 그룹(JJUG)에서 개최하는 Cross Comunity Conference 2008 에서 Scala를 주제로 한 세션이 2개 열리는군요. 내용은 당연히 일본어로만 진행된답니다-_-

http://www.java-users.jp/contents/events/ccc2008spring/


16:30 - 17:20 A-5 Scala for enterprise application empowerd by Lift
by Ushio Tsuyoshi(Mamezou, Co.)

17:40 - 18:30 BOF-1 Post-Java? Possibilities of JVM based functional OO
language Scala
by Hanyuda Eiiti (Mamzou, Co.)
   Mizushima Kouta (Tsukuba university, Computer Science)
   Seki Takashi (TBS television, Digital center)

참고: 엔터프라이즈 어플리케이션 개발로 소개하는 Lift는 Scala 기반의 웹프레임워크라고 생각하시면 됩니다.

Scala 이상한 점

Posted 2008. 4. 22. 19:48
마소 원고 쓰면서 Scala를 가지고 놀다가 재밌는 사례를 발견했습니다.

val은 보통 변하지 않는 값(immutable)을 선언할 때 쓰는 키워드인데, 다음과 같이 선언을 했더니 오류가 발생하지 않고 x 가 1이 되더군요.

scala> val x: Int = x + 1
x: Int = 1


우변의 x + 1은 x가 아직 초기화 되지 않았기 때문에 오류가 발생해야 맞을 것 같은데, 신기하게 그냥 0으로 초기화되고 1을 더해서 x의 값은 1이 되었습니다. scala-user 메일링 리스트에 물어봤더니, Scala 인터프리터에서 위와 같이 선언하면 다음과 같은 클래스를 생성한다고 합니다.


class Foo {
     int x;
     public Foo() {
         x = x + 1;
     }
 }

x가 필드가 되기 때문에 x은 자동으로 0이 할당되고, x + 1은 1이 됩니다. 이건 구현상의 이슈고, Scala에서는 적절히 오류가 발생해야 맞을 것 같은데, 언어 명세에 어떻게 정의해놨는지 모르겠군요.


Scala에서 Parital Application

Posted 2008. 4. 19. 20:34
scala.user 메일링 리스트에 partial application에 대한 질문이 올라왔군요.

def m(a: Int, b: Int, c: Int) = a + b + c


와 같이 함수 m을 정의한 후에 a와 c의 값을 정해주고 b만 인자로 받는 새로운 함수를 만들어내는 문제입니다.

Scala에서 이 문제에 대한 해답은

scala> def m(a: Int, b: Int, c:Int) = a+b+c
m: (Int,Int,Int)Int

scala> val f=m(1, _:Int, 3)
f: (Int) => Int = <function>

scala> f(2)
res0: Int = 6


Scala에서 _ 는 partial evaluation을 하도록 만들어 줍니다.

참고: partial application과 비슷한 용어로 커링(curring)은 (A, B) => C를 A => (B => C)로 만들어 주는 방법을 말합니다. parital application과 커링은 구분해서 사용해야 합니다. LtU의 Currying != Generalized Partial Application?! 을 참조하세요.

Scala

Posted 2008. 4. 19. 17:09
5월 마소에 Scala 프로그래밍 언어를 소개합니다. Scala는 객제+함수언어를 지향하며 자방 가상 머신(JVM) 위에서 돌아가는 언어입니다. 얼마 전에 블로그에서 한 번 소개를 한 적이 있었습니다.

사실 기본적인 문법 및 HelloWorld 스타일의 예제만 소개만 해서는 어떤 언어나 비슷하기 때문에 Scala의 여러 특징 중에 어떤 점을 부각해서 글을 작성할지 고민을 했습니다. 일단 머리 속에 떠오른 몇 가지 아이디어는 다음과 같았습니다.

1) 자바 플랫폼 언어
 * 기존 라이브러리와의 통합
 * 컴포넌트 활용

2) 함수형 언어
 * 함수 언어의 장점
 * 함수 언어와 객체지향 언어와의 통합

3) 병렬 프로그래밍
 * Actor 모델
 * 다양한 병렬 프로그래밍 도구


자세한 내용은 5월 호에^^;

Scala

Posted 2008. 3. 27. 01:50
2008년은 함수형 언어가 우리 개발자들의 품으로 한 걸음 더 다가오는 해가 아닐까 싶습니다. .NET에서는 이미 OCaml의 .NET 버전인 F#을 내놓고 열심히 홍보를 하고 있고 자바 진영에서도 JVM을 타겟으로 하는 함수형 언어인 Scala가 조금씩 홍보를 시작한 것 같습니다. (개발 자체는 2001년에 EPFL에서 이미 시작되었지만요.) 마침 올해 초에 IBM developerWorks에 The busy Java developer's guide to Scala: Functional programming for the object oriented 라는 튜토리얼 형태의 글이 올라오기도 했습니다.

구글에서는 이미 2006년에 Scala를 만든 Martin Odersky를 초청해서 The Scala Experiment: Better Language Support for Component Systems? (PDF)라는 제목으로 Scala에 대한 강연을 하기도 했습니다. 참고로 Martin Odersky는 자바 진영에서 굉장히 유명한 사람인데 자바의 제네릭스를 추가하기 위한 Pizza, GJ 프로젝트에 참여했고 이 프로젝트는 결국 자바 5의 제네릭스가 되었습니다. 스스로가 함수 언어에 대한 이론을 기반으로 자바에 실용적인 활용 방안을 모색해 온 사람이라 Scala에도 이런 철학이 그대로 녹아들어가 있습니다.

Scala의 기본적인 아이디어는 함수형 언어의 특징을 객체지향 언어인 자바에 잘 집어넣자는 것입니다. 대신 Pizza나 GJ 처럼 자바 언어를 어느 정도 개선하는 스타일이 아니라 아주 새로운 언어를 만들되 타겟만 JVM으로 하는 방식을 취했습니다. Scala의 초기 논문을 보면 언어의 출발은 Java의 복잡한 컴포넌트를 쉽게 조합할 수 있는 도구에 있었습니다. 컴포넌트 재사용의 편의는 고차함수나 Algebraic Data Type 등 함수 언어 아이디어를 OO와 접목하는 걸로 해결하려고 했고요.

코드는 대충 아래와 같이 생겼습니다. 정작 타이핑을 하는 언어임에도 불구하고 타입 추론의 큰 도움으로 마치 동적인 스크립트 언어 같은 느낌을 줍니다. 실제로 같은 일을 하는 자바 코드에 비해 1/2, 1/3 정도로 코드량이 주는 것을 확인하실 수 있습니다.


object Timer
{
def periodicCall(seconds: Int, callback: () => Unit): Unit =
{
while (true)
{
callback()
Thread.sleep(seconds * 1000)
}
}

def main(args: Array[String]): Unit =
{
periodicCall(1, () =>
Console.println("Time flies... oh, you get the idea."))
}
}


개인적으로 JVM을 타겟으로 하는 프로젝트를 할 일이 생기면 과감히 Scala를 써볼 생각도 있습니다. Scala에 대한 구체적인 소개는 다음 기회에 자세히 다뤄보려고 합니다. 미리 궁금하신 분은 Scala 홈페이지를 방문해주세요.