IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  자바  >

바쁜 자바 프로그래머를 위한 스칼라 입문: 객체 지향론자를 위한 함수 프로그래밍

스칼라(Scala)가 어떻게 객체 지향과 함수 프로그래밍의 장점만을 아울렀는지 살펴보자

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

영어원문

영어원문


제안 및 의견
피드백

난이도 : 초급

Ted Neward, Principal, Neward & Associates

옮긴이: 김도형 dwkorea@kr.ibm.com

2008 년 4 월 15 일

자바(Java™) 플랫폼은 전통적으로 객체 지향 프로그래밍의 영향권이었습니다. 하지만 최근에는 충실한 자바 언어 지지자조차도 애플리케이션 개발에 있어 가장 최근의 복고 추세인 함수 프로그래밍에 관심을 가지기 시작했습니다. 이번에 시작하는 연재에서는 함수와 객체 지향 기법을 혼합한 JVM을 위한 프로그래밍 언어인 스칼라를 소개합니다. 언어 소개와 함께 동시성(concurrency) 등 굳이 스칼라를 배워야 하는 이유를 설명하고, 스칼라를 얼마나 빨리 써먹을 수 있는지를 살펴봅니다.

사람들은 첫사랑을 잊지 못한다.

필자의 첫사랑은 Tabinda (Bindi) Khan이었다. 당시, 정확히는 7학년(역주: 한국의 중1) 이후 몇 년간이 내 유년기의 번영기였다. 그녀는 예뻤고, 영리했으며, 무엇보다 10대였던 내 어설픈 농담에도 웃어줬다. 당시 기억으로 7, 8학년 대부분 동안 우리는 종종 데이트하곤 했다. 하지만 9학년이 되어서는 서로 소원해졌는데, 사실 그녀가 2년간 똑 같은 어설픈 농담을 듣는 데 싫증이 났다는 걸 완곡하게 표현하면 그렇다. 난 그녀를 잊지 못할 것이다(특히 10년만에 고등학교 동창회에서 서로 반갑게 달려가 다시 만났다는 것 때문에라도). 하지만 더 중요한 것은, 다소 과장됐을 수도 있지만, 그 소중한 기억을 결코 잊지 않을 것이라는 점이다.

이 연재에 대해

저자인 Ted Neward는 이 연재를 통해 여러분에게 스칼라 프로그래밍 언어를 심층적으로 설명한다. 이 새 developerWorks 연재 를 통해 최근의 스칼라를 둘러싼 떠들썩한 호평의 실체를 살펴보고, 스칼라의 언어적 특성의 일부가 실제 어떻게 쓰이는지도 배우게 될 것이다. 비교가 필요할 때는 항상 스칼라 코드와 자바 코드를 나란히 보일 예정이다. 하지만 곧 알게 되겠지만 스칼라에 있는 많은 요소는 자바 언어와 직접적인 관계가 없는 것들이고, 이런 부분이 스칼라를 매력적으로 만든다. 그렇다면 자바 코드로도 할 수 있다면 왜 힘들여 스칼라를 배울까?

자바 프로그래밍과 객체 지향은 많은 프로그래머에게 첫사랑이다. 그래서 필자가 Bindi에게 줬던 것 같은 존경과 온전한 사랑으로 대한다. 어떤 개발자는 자바 프로그래밍이 메모리 관리와 C++라는 지옥의 유황불 구덩이에서 자신을 구원했다고 말할 것이고, 또 다른 개발자는 절차적(procedural) 프로그래밍의 깊은 절망에서 끌어 올려줬다고 할 것이다. 심지어 자바로 객체 지향 프로그래밍을 하는 것은 그저 "우리가 항상 일을 처리해 온 그대로"라고 할 개발자도 있을 것이다. (이봐요~ 만약 우리 아버지, 할아버지 때도 객체 지향 프로그래밍이 있었다면 그랬겠죠.)

하지만 궁극적으로 어떤 첫사랑이든 시간이 지나면 극복된다. 그리고 새로운 사랑을 찾아 옮겨갈 시간이 오기 마련이다. 감정은 바뀌었고 이야기의 주인공은 성숙해졌다(그리고 바라건대 새 농담이라도 몇 개 배웠을 것이다). 하지만 더욱 중요한 것은 우리 주변의 세상이 변했다는 것이다. 많은 자바 개발자가 자바 프로그래밍을 사랑하는 만큼 우리 개발에서 새로운 기회를 잡을 때고 그 기회로 무엇을 할 수 있을 지를 깨닫고 있다.

난 영원히 당신을 사랑할 겁니다…

지난 5년 간에 자바 언어에 대한 불만이 점차 커지고 있다. 혹자는 루비 온 레일즈(Ruby on Rails)의 성장을 주요 요인으로 지적하기도 하지만 개인적으로는 RoR(루비 전문가 들이 흔히 지칭하는 대로)은 그저 결과일 뿐 원인은 아니라고 생각한다. 또는 더 정확하게 말해 자바 개발자들은 더 깊고 은밀한 이유의 결과가 루비일 거라고 생각한다.

간단히 말해 자바 프로그래밍은 오래된 만큼 한계를 보이고 있다.

혹은 더 정확히 말하면 자바 언어 가 한계를 보이고 있다.

생각해 보자. 처음 자바 프로그래머가 나타났을 때 클린턴(대통령) 재임 중이었고 인터넷을 일상적으로 사용하는 사람은 여전히 진짜 괴짜들뿐이었다. 집에서 인터넷에 접속하는 유일한 방법은 전화로 접속하는 방법뿐이었기 때문이다. 블로그라는 개념도 없었고 모든 사람은 상속이 재사용의 근본적인 방법이라고 믿었다. 우리 또한 객체가 실세계를 모델링하는 가장 좋은 방법이고 무어의 법칙은 기하급수적으로 영원히 유효할 거라고 믿었다.

실제 업계의 많은 사람이 신경 썼던 건 무어의 법칙이다. 하지만 2002/2003년 이후, 마이크로프로세서의 주된 추세는 멀티 코어 CPU가 되었다. 본질적으로는 하나의 칩에 다수의 CPU를 넣은 것이다. 이는 매 18개월마다 CPU 속도가 2배가 될 거라고 예상한 무어의 법칙과 다르다. CPU 하나에서 표준적인 라운드 로빈 주기(round-robin cycle)를 반복하기보다는 2개의 CPU에서 동시에 실행하는 멀티스레드 환경이 됐다는 것은 코드가 제대로 동작하려면 멀티스레드에 완벽하게 안전해야 한다는 걸 뜻한다.

학계에서는 이 문제에 대해 많은 연구가 되어 왔고 그 결과로 많은 언어가 설계되었다. 치명적인 문제는 이런 언어 대부분이 각자 그 언어 전용의 가상 기계(virtual machine)나 해석기(interpreter) 위에서 구현되었기 때문에 적용을 하려면, 루비처럼 새로운 플랫폼으로 옮겨가야 했다는 점이다. 멀티스레드 실행은 중요한 문제이고 새로운 언어 중 일부는 강력한 해법을 제시한다. 하지만 너무나 많은 회사나 기업이 10년 전 C++에서 자바 플랫폼으로 이행했을 때를 기억한다. 새로운 플랫폼으로 옮겨가는 위험 부담은 많은 회사가 이제 진지하게 검토하지 않을 정도로 높다. 사실 많은 회사나 기업이 아직 자바 플랫폼으로 이행할 때 입은 상처를 치료하고 있다.

스칼라로 오십시오.




위로


스칼라 - 확장성 있는 언어(A SCAlable LAnguage)

왜 스칼라인가?

새로운 프로그래밍 언어를 배우는 것은 항상 만만치 않은 일이다. 스칼라가 수용한 함수적 접근법처럼 문제 접근 방법에 있어 완전히 새로운 사고 방식을 필요로 하는 언어는 더욱 그렇고, 스칼라에서 객체 지향과 함수적 개념을 녹여 넣은 것처럼 다른 접근법을 혼용한다면 더욱 만만치 않다. 스칼라를 배우는 데는 시간이 필요하고, 설사 바쁜 일정에 짬을 내 스칼라를 배울 마음을 먹었더라도 얼핏 봐서는 투자한 만큼 효과를 볼 수 있을 거라는 확신을 가지기 어려울 수도 있다. 필자가 보장한다. 스칼라는 많은 흥미로운 특징을 제공한다. 이 연재에서는 그런 특징을 차차 다룰 것이다. 스칼라를 사용할 때 얻는 이점을 맛 보여주기 위해 몇 가지 특징을 정리해 봤다. 스칼라로는 다음과 같은 일을 할 수 있다.

  • 루비처럼 내부 용도로 사용할 DSL(역주: Domain Specific Language, 특정 분야에서 정보를 기술하기에 최적화된 언어를 뜻한다) 들을 만들 수 있다. 식별자(identifier)에 있어 유연한 스칼라의 특징 때문에 이런 일이 가능하다.

  • 아주 확장성 있고 동시 수행하는(concurrent) 데이터 처리기를 만들 수 있다. 스칼라에서는 "모든 상태는 기본적으로는 변경할 수 없다(immutable-state-by-default)"는 원칙 때문이다.

  • 같은 일을 하는 자바보다 짧은 코드를 작성할 수 있게 해 준다. 클로저나 암묵적 정의 같은 스칼라의 문법적 특징 덕택에 보통 코드가 같은 자바 코드의 반이나 2/3 정도다.

  • 병렬 하드웨어 구조를 활용할 수 있게 해 준다 (멀티 코어 CPU 등). 함수적 설계를 장려하는 스칼라의 특성 때문에 병렬 하드웨어를 활용하기 좋다.

  • 더 큰 코드를 이해할 수 있게 해 준다. 본질적으로 "모든 것은 객체"이도록 한 타입 원칙의 단순화 덕택이다.

스칼라는 분명 프로그래밍에 대한 강력하고 새로운 관점을 제시한다. 스칼라를 컴파일해 JVM에서 실행할 수 있다는 사실은 스칼라를 "실제 작업"에서 사용하는 것을 아주 쉽게 해 줄 뿐이다.

스칼라는 다음 같은 몇 가지 강력한 장점을 가진 함수-객체 혼성 언어다.

  • 첫째, 스칼라는 자바 바이트코드로 컴파일되고 JVM 위에서 실행된다. 이 때문에 자바의 풍부한 오픈 소스 환경을 활용할 수 있을 뿐 아니라 별도의 이행 비용 없이 현재의 IT 환경에 통합할 수 있다.

  • 둘째, 스칼라는 하스켈(Haskell)과 ML의 함수 원리에 기반을 두고 있으면서도 여전히 자바 프로그래머가 좋아하는 친숙한 객체 지향 개념을 많이 빌어왔다. 결과적으로 양쪽의 최선을 하나로 녹여냈다. 따라서 이미 친숙한 개념을 버리지 않고도 막대한 이점을 얻을 수 있다.

  • 마지막으로 스칼라는 자바 커뮤니티에서 Pizza와 GJ 언어로 잘 알려진 Martin Odersky에 의해 개발되었다. 참고로 GJ는 현재 Java 5 제네릭스(Generics)의 프로토타입이다. 이 같이 스칼라는 뭔가 "진지한 성과"라는 느낌을 준다. 별 생각 없이 만들어 별 생각 없이 버려지는 언어가 되지는 않을 것이다.

스칼라라는 이름의 뜻대로 매우 확장성 있는 언어이다. 확장성에 대해서는 이 연재를 좀 더 진행한 후에 언급하겠다.

스칼라를 다운로드하고 설치하기

스칼라 배포판은 스칼라 웹 사이트에서 다운로드 할 수 있다. 이 글을 쓸 당시 최신 릴리스는 2.6.1-final이었다. 자바 설치 파일, RPM/데비안 패키지, 단순히 목표 디렉터리에 풀어 놓으면 되는 gzip/bz2/zip 배포판과 새로 컴파일할 수 있는 소스 tar 묶음 형태로 각각 배포된다. (데비안 사용자의 경우 간단한 "apt-get install"로 데비안 웹 사이트에서 2.5.0-1판을 받아 설치할 수 있다. 하지만 2.6판은 약간 차이점이 있으므로 스칼라 웹 사이트에서 직접 다운로드해 설치하기를 권한다.)

스칼라를 원하는 디렉터리에 설치하자. 필자의 경우 이 글을 윈도우 (Windows®) 환경에서 쓰고 있는데 C:/Prg/scala-2.6.1-final에 설치했다. 다음으로 SCALA_HOME 환경 변수를 이 디렉터리로 지정하고, SCALA_HOME\binPATH 에 넣어 명령창에서 쉽게 실행할 수 있도록 하자. 제대로 설치되었나 보기 위해 명령행에서 " scalac-version "이라고 치자. 이 때 설치한 스칼라 판 번호인 2.6.1-final이 표시되어야 한다.




위로


함수 개념

시작하기 전에 왜 스칼라가 그런 형태고 그렇게 움직이는지를 이해하기 위해 필요한 몇 가지 함수 개념을 설명하겠다. 만약 하스켈, ML이나 최근의 F# 등 언어를 써 봤다면 다음 절로 건너 뛰어도 좋다 .

함수 언어는 프로그램이 수학 함수처럼 동작해야 한다는 개념 때문에 그런 이름이 붙여졌다. 바꿔 말하면 입력 값 집합이 주어지면 함수는 항상 같은 출력을 내놓아야 한다. 이는 모든 함수가 값을 돌려줘야 한다는 것뿐 아니라 이번 호출과 다음 호출 사이에 고유의 상태를 지녀서는 안 된다는 것을 뜻한다. 상태가 없다는 이 기본적인 개념이 함수/객체 세상으로 넘어오면 객체는 기본으로 변경할 수 없다는 뜻이 된다. 이런 점이 함수 언어가 많은 동시성(concurrency)이 필요한 요즘 구세주로 각광 받는 이유다.

최근에 자바 플랫폼에 자리 잡기 시작한 많은 동적 언어와 달리 스칼라는 자바처럼 정적으로 타입 검사를 하는 언어다. 하지만 자바 플랫폼과 달리 프로그래머의 도움 없이 컴파일러가 코드를 분석해 특정 값이 무슨 타입인지 알아내는 타입 추론(type inference) 을 많이 사용한다. 타입 추론을 사용하면 타입 정보를 써 줘야 하는 경우가 줄어든다. 예를 들어 Listing1에서 보듯 자바 코드는 지역 변수를 선언하고 값을 대입해야 한다.


Listing 1. javac의 능력(에휴~)
                
class BrainDead {
  public static void main(String[] args) {
    String message = "Why does javac need to be told message is a String?" +
      "What else could it be if I'm assigning a String to it?";
  }
}
			

나중에 보겠지만 스칼라는 그런 조합이 필요 없다.

패턴 매칭(pattern matching) 같은 많은 다른 함수적 특성이 스칼라에 도입되었다. 하지만 함수적 특징을 다 열거하기에는 아직 좀 이른 것 같으니 조금만 참자. 또한 스칼라는 연산자 중복 정의(알게 되겠지만 대부분 자바 프로그래머가 상상하는 것과는 전혀 다르다), "타입의 상계와 하계(upper and lower bounds)"를 고려한 제네릭스(generics), 뷰(view) 등 현재 자바 프로그래밍에 없는 몇 가지 특성을 추가했다. 이런 특성은 스칼라가 XML을 처리하거나 생성하는 작업 같은 특정 작업을 매우 쉽게 처리할 수 있게 해 준다.

이제 추상적인 소개는 충분한 것 같다. 프로그래머는 코드를 봐야 직성이 풀리니 이제 스칼라가 뭘 할 수 있는지를 살펴 보자.




위로


당신을 알아 갑니다.

첫 스칼라 프로그램은 컴퓨터 공학의 신이 지정한 표준 데모 프로그램인 Hello World다.


Listing 2. Hello.scala
                
object HelloWorld {
  def main(args: Array[String]): Unit = {
    System.out.println("Hello, Scala!")
  }
}
			

scalac Hello.scala 로 컴파일해 나온 코드를 스칼라 실행 명령( scala HelloWorld )이나 기존 자바 실행 명령으로 실행해 보자. 단, 기존 자바 실행 명령을 사용할 때는 JVM의 클래스 경로에 스칼라의 코어 라이브러리를 포함해야 한다( java -classpath %SCALA_HOME%\lib\scala-library.jar;. HelloWorld ). 어떤 방법을 쓰든 전통적인 인사인 "Hello, Scala!"가 나타나야 한다.

Listing 2의 어떤 부분은 친숙하게 보이겠지만 완전히 새로운 부분도 눈에 띤다. 예를 들어 친숙한 System.out.println 로 스칼라가 하부 자바 플랫폼에 충실하다는 것을 알 수 있다. 스칼라는 자바 플랫폼의 모든 측면을 활용할 수 있도록 하고 있다. (실제 스칼라 타입이 자바 클래스에서 상속받거나 반대로 자바 클래스가 스칼라 타입에서 상속받을 수도 있다. 여기에 대해서는 추후 설명하겠다.)

반면 관찰력이 좋다면 알아챘겠지만 System.out.println 뒤에 세미콜론이 없다. 이는 오타가 아니다. 자바 플랫폼과 달리 행이 끝에서 문장이 끝나는 것이 명백한 경우 세미콜론을 적지 않아도 된다. 하지만 여전히 세미콜론을 써도 되는데, 예를 들어, 한 행에 여러 문장을 쓸 때는 반드시 세미콜론이 있어야 한다. 초급 스칼라 프로그래머의 경우 대부분 세미콜론을 안 써도 될 것이다. 필요한 경우 컴파일러가 눈에 잘 띄는 에러 메시지로 언질을 줄 테니 말이다.

또한 중요한 것은 아니지만 스칼라에서는 클래스 정의를 담은 파일 이름이 클래스 이름과 달라도 된다. 혹자는 이 점을 자바에서 달라진 참신한 변화라고 생각할 것이다. 하지만 설사 이런 특성을 좋아하지 않더라도 예전 클래스 이름과 파일 간의 명명 규약을 사용하는 데는 아무런 문제가 없다.

이제 기존 자바/객체 지향 코드 대비 스칼라가 진짜 달라지기 시작하는 부분을 살펴보자.




위로


기능과 형식, 드디어 한꺼번에 두 마리의 토끼를 잡다

먼저 자바 애호가라면 HelloWorld 가 " class " 대신 키워드인 object 로 정의되었다는 점을 알아챌 을 것이다. 이는 싱글톤 패턴(singleton pattern)이 널리 사용되는 데 대한 스칼라의 배려다. 즉 object 키워드는 해당 클래스가 싱글톤 객체로 쓰일 것이라는 것을 컴파일러에 알려준다. 그 결과 스칼라는 HelloWorld 객체가 단 하나만 존재하도록 보장한다. 같은 이유로 자바와는 달리 main 을 정적 메서드로 정의하지 않았다는 점을 유의한다. 아예 스칼라는 " static "의 사용을 피한다. 만약 애플리케이션에 특정 타입에 해당하는 "전역(global)" 객체도 있어야 하고 해당 타입의 다른 객체들도 있어야 하는 경우, 스칼라에서는 같은 이름으로 동시에 classobject 를 정의할 수 있다.

다음으로 main 메서드는 자바와 마찬가지로 스칼라 프로그램의 시작점이다. 정의 자체는 자바와 달라 보이지만 사실상 같다. 즉 String 의 배열 하나를 인자로 받고 아무것도 돌려주지 않는다. 하지만 스칼라에서는 정의 부분이 약간 달라 보인다. 예제에서 args 인자는 args: Array[String] 으로 정의한다.

스칼라에서 배열은 제네릭화된(genericized) Array 클래스의 객체로 표현한다. 여기서 스칼라에서 타입 인자를 나타내기 위해 "<>" 대신 "[]"를 사용한다는 것을 알 수 있다. 또 언어 전체에서 일관성 있게 " name:type " 형태를 사용한다.

다른 전통적인 함수 언어처럼 스칼라에서는 함수는 반드시 값을 하나 돌려줘야 한다(스칼라의 경우 함수가 아니라 메서드다). 그래서 "값이 아닌(non-value)" 값인 Unit 을 돌려준다. 자바 프로그래머 입장에서는 실질적으로, 최소 당분간은, 대부분의 경우 Unitvoid 와 같다고 생각해도 무방하다.

메서드 정의 문법은 다소 흥미로운데 main 식별자(identifier)에 다음에 오는 메서드 몸체를 대입하는 것처럼 " = " 연산자를 사용한다. 사실이 그런데 함수 언어에서는 함수를 변수, 상수처럼 일급 개념(first class concept)으로 취급하기 때문에 문법적으로도 마찬가지다.




위로


클로저요?

함수를 일급 개념으로 취급하게 된다는 것은 함수를 최근 자바 커뮤니티에서 논쟁이 한창인 클로저(closure) 로 알려진 하는 독립 구문으로 다뤄야 함을 의미한다. 스칼라에서는 쉽게 된다. 클로저의 능력을 보기 전에 Listing 3 의 간단한 스칼라 프로그램을 보자. 여기서 oncePerSecond() 함수는 내부 로직(System.out에 출력)을 1초에 한번씩 반복한다.


Listing 3. Timer1.scala
                
object Timer
{
  def oncePerSecond(): Unit =
  {
    while (true)
    {
      System.out.println("Time flies when you're having fun(ctionally)...")
      Thread.sleep(1000)
    }
  }

  def main(args: Array[String]): Unit =
  {
    oncePerSecond()
  }
}
			

이 코드는 유용하기는 하지만 애석하게도 그다지 함수적이지 않다. 예를 들어 표시될 메시지를 바꾸려고 하면 oncePerSecond 메서드의 몸체를 수정해야 한다. 이 때 통상적인 자바 프로그래머라면 oncePerSecond 에 표시할 메시지를 담은 String 인자를 넘겨 받도록 할 것이다. 하지만 이조차 명백한 한계가 있다. 주기적으로 수행하는 다른 작업(원격 서버가 살아 있는지 ping하는 것 같은)을 하려면 각기 별도의 oncePerSecond 가 있어야 한다. 이는 반복금지의 원칙(Don't Repeat Yourself / DRY)에 명백히 반한다. Listing 4에서 보듯 클로저는 유연하고 강력한 대안이 된다.


Listing 4. Timer2.scala
                
object Timer
{
  def oncePerSecond(callback: () => Unit): Unit =
  {
    while (true)
    {
      callback()
      Thread.sleep(1000)
    }
  }

  def timeFlies(): Unit = 
  { Console.println("Time flies when you're having fun(ctionally)..."); }

  def main(args: Array[String]): Unit =
  {
    oncePerSecond(timeFlies)
  }
}
			

이제 뭔가 흥미로워 지기 시작한다. Listing 4에서 oncePerSecond 는 인자를 가지지만 인자 타입이 특이하다. 엄격하게 말해 callback 인자는 함수를 인자로 받는다. 함수 중에서도 인자가 없고("()"로 나타낸다), 아무 값도 돌려주지 않는(값을 돌려준다는 것은 "=>", 아무 값도 아니라는 것은 함수적인 값 "Unit"으로 나타낸다) 경우만 받아 들인다. 이어 루프 몸체에서 callback 인자를 사용해 넘어온 함수를 호출하는 과정을 주의 깊게 살펴 보기 바란다.

다행히 해당 프로그램에 timeFlies 라는, 인자를 받지 않고 값을 돌려주지 않는 함수가 있다. 그래서 main 에서 해당 함수를 oncePerSecond 함수에 넘긴다. (여기서 timeFlies 는 스칼라에서 정의한 Console 이라는 클래스를 사용하고 있다. 이 클래스는 기본적으로 System.out 이나 새 java.io.Console 클래스와 같은 목적으로 쓰인다. 이는 순전히 미적인 문제라서, 이 경우 System.out 이나 Console 을 대신 사용해도 차이가 없다.)




위로


이름 없는 Function(무명 함수), 네 Function(기능)은 뭐지?

이제 timeFlies 함수가 다소 낭비로 보인다. 결국 oncePerSecond 에 넘어가는 것 말고는 아무런 목적이 없다. 그래서 Listing 5에서는 엄격하게는 해당 함수를 정의하지 않을 것이다.


Listing 5. Timer3.scala
                
object Timer
{
  def oncePerSecond(callback: () => Unit): Unit =
  {
    while (true)
    {
      callback()
      Thread.sleep(1000)
    }
  }

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

Listing 5에서 main 함수는 oncePerSecond 의 인자로 리스프(Lisp)나 스킴(Scheme)의 람다식(lambda expression) 같이 임의의 코드 블록을 넘겨주고 있다. 람다식은 사실 다른 종류의 클로저다. 이 무명 함수(anonymous function) 는 함수를 언어의 일급 시민으로 취급할 때의 유용성을 보여주는 다른 예다. 상속과 완전히 다른 차원에서 코드를 일반화할 수 있게 해 준다. (독자가 전략 패턴(Strategy pattern)을 즐겨 쓴다면 이미 주체할 수 없을 정도로 군침을 흘리고 있을 것이다.)

사실, callback이 1초에 한번 불린다는 비합리적인 제약을 두고 있다는 점에서 oncePerSecond 는 너무 제약적이다. Listing 6처럼 주어진 함수를 얼마나 자주 호출할지를 나타내는 두 번째 인자를 추가하면 해당 함수를 더욱 일반화할 수 있다.


Listing 6. Timer4.scala
                
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."))
  }
}
			

이런 형태는 함수 언어에서는 일반적이다. 즉 특정 작업을 하는 고수준 추상 함수를 만들고, 그 함수가 코드 블록(무명 함수)을 인자로 받도록 한다. 그리고 해당 고수준 함수 안에서 주어진 코드 블록을 호출한다. 예를 들어 객체 컬렉션(collection)의 내부 객체를 처리할 때 for 루프 내에서 통상적인 자바 반복자(iterator) 객체를 사용하기보다는, 해당 컬렉션 객체에 인자 하나(처리할 객체)를 받는 함수를 인자로 받는 "iter" 혹은 "map"이라는 이름의 함수를 정의하는 함수 라이브러리를 사용한다. 예를 들면 앞서 언급한 Array 클래스에는 Listing 7에 보인 것 같은 filter 라는 함수가 있다.


Listing 7. Array.scala의 일부
                
class Array[A]
{
    // ...
  	def filter  (p : (A) => Boolean) : Array[A] = ... // not shown
}
			

Listing 7에서 인자 p 는 타입 인자 A 로 나타낸 타입의 인자를 받고 boolean을 돌려주는 함수다. 스칼라 문서에는 filter 가 "이 배열의 요소 중 조건식(predicate) p를 만족하는 모든 요소를 담은 배열을 돌려준다"라고 되어 있다. 이를 활용하면 이전의 Hello World 프로그램에서 "G"로 시작하는 명령행 인자를 모두 찾아야 할 때 Listing 8처럼 간단한 코드로도 충분하다.


Listing 8. Hello, G-men!
                
object HelloWorld
{
  def main(args: Array[String]): Unit = {
    args.filter( (arg:String) => arg.startsWith("G") )
        .foreach( (arg:String) => Console.println("Found " + arg) )
  }
}
			

여기서 filter 는 boolean값을 돌려주는( startsWith() 의 결과) 무명 함수인 조건식을 받고 " args " 배열의 모든 요소마다 해당 조건식을 호출한다. 먄약 조건식이 true를 돌려주면 해당 요소는 결과 배열에 추가된다. 배열 전체를 처리한 후, 결과 배열을 돌려주는데, 이 결과는 곧바로 "foreach" 호출의 소스가 된다. " foreach " 함수는 이름 그대로 다른 함수를 받아 배열 내 각 요소에 그 함수를 적용한다(이 경우 단순히 해당 요소를 화면에 표시한다).

HelloG.scala 에 해당하는 자바 코드가 어떨 것이라는 것을 상상하기는 그다지 어렵지 않다. 또, 스칼라로 작성된 코드가 아주, 아주 짧고, 명료할 거라는 점도 명백하다.

요약

스칼라로 프로그래밍하는 것은 흥미롭게도 익숙하면서도 동시에 다르다. 수년 간 익혀 친숙한 동일한 코어 자바 객체를 사용한다는 면에서 비슷하기도 하지만, 프로그램을 부분으로 나누는 방법에 있어서는 명백히 다르다. 이 연재의 첫 편에서는 스칼라로 뭘 할 수 있는지를 간략히 살펴 봤다. 아직 가야 할 길이 멀지만 당분간은 함수 프로그래밍을 즐겨보면 어떨까?



참고자료

교육

제품 및 기술 얻기
  • Scala : 스칼라를 다운로드해 이 연재를 따라가며 배워보자.

토론


필자소개

Ted Neward photo

Ted Neward는 Neward & Associates의 사장으로 자바, .NET, XML 서비스와 다른 플랫폼에 대한 컨설팅, 조언, 교육, 강연을 한다. 워싱턴 주 시애틀 근처에 산다.




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



 


 


 


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us





위로


Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States, other countries, or both. 기타 회사, 제품, 및 서비스명은 다른 상표나 서비스 마크일 수 있습니다.

developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의