다음과 같은 스칼라 클래스 파일이 있고, foo()
라는 메소드가 정의 되어 있습니다.
//Foo.scala
class Foo {
def foo() = "foo"
}
또, 다음과 같은 클래스 파일이 있습니다. 메소드 대신 bar
라는 함수를 정의 했습니다.
//Bar.scala
class Bar {
val bar = () => "bar"
}
foo()
메소드와 bar
함수는 거의 비슷해 보이지만, scala
컴파일 하게 되면 중요한 차이를 보여줍니다.
Foo.class
Bar.class
Bar$$anonfun$1.class
Bar
가 컴파일된 클래스 파일을 보면. 자바에 경험이 있으신 분은 아시겠지만$$annonfun
는 이너 클래스를 의미합니다. val bar = () => "bar"
부분이 이너클래스로 변환되어서 객체화 되었습니다. 그래서, 마치 함수가 객체처럼 사용되어 파라미터로 전달되고, 리턴 되는 것입니다.
스칼라에서 메소드는 클래스의 멤버로써 존재하는 것이고, 함수는 독립적인 객체처럼 취급됩니다.
위의 부분은 잠시 잊고 메소드와 차이를 좀 더 살펴보겠습니다.
파라미터를 받지 않는 함수는 다음처럼 작성합니다. 파라미터를 전달 받지 않으므로 빈 ()
를 이용해서, 0개의 파라미터(0-arity)를 표현합니다. (()
표현은 Unit
을 표현하는 부분과 종종 혼동됩니다. 여기서는 단순히 빈 파라미터를 나타내는 것이라고 생각하세요. )
val fMsg = () => "Hello" //> () => String
마찬가지로 파라미터를 받지 않는 메소드도 다음 처럼 작성 할 수 있습니다.
def mMsg() = "Hello" //> ()String
위 코드에서 mMsg()
메소드의 ()
는 생략이 가능합니다. 반면, 빈 파라미터를 받는 함수의()
는 생략이 불가능합니다.
메소드 mMsg
와 함수fMsg
의 "Hello"
메시지를 각각 출력하고 싶습니다. 그래서 다음처럼 작성합니다.
println(mMsg)
//> "Hello"
println(fMsg)
//> <function0>
메소드를 이용한 부분은 정상적으로, "Hello"
라는 메시지가 출력되지만, 함수를 이용한 부분은, 예상과 다르게(?) <function0>
이라는 결과가 출력되네요.
위의 함수에서 "Hello"
라는 값을 도출하기(evaluation) 위해서는 다음처럼 빈 파라미터()
를 붙여줍니다.
println(fMsg())
//> Hello
정상적으로 출력이 되네요. 여기서 알 수 있는 것은 다음과 같습니다.
그래서 다음의 foo
는 완전히 같은 함수를 가리키게 됩니다.
val foo = fMsg //> foo : () => String = <function0>
반면, 다음의 bar
는 메소드가 호출 된 결과인 “Hello” 값을 갖게 됩니다.
val bar = mMsg //> bar : String = "Hello"
또 다른 특성을 살펴보기 위해서, () => String
타입을 전달 받아 함수의 리턴 값을 출력하는 메소드를 하나 만들어 보겠습니다. 그리고, “Hello”를 리턴하는 함수와 메소드를 하나씩 정의하겠습니다.
def myJet(s: () => String) = {
println(s())
}
val fMsg = () => "Hello"
def mMsg() = "Hello"
함수와 파라미터의 타입이 맞으니 정상적으로 전달 할 수 있습니다.
myJet(fMsg)
//> Hello
그런데 다음처럼 메소드
를 전달하는 것(처럼 보이는…)도 가능합니다.
myJet(mMsg)
//> Hello
메소드는 객체의 멤버일 뿐, 일급함수가 아니라서 파라미터로 전달이 불가능합니다. 이러 한 것이 가능한 이유는 스칼라에서 자동적으로 메소드를 일급함수(first-class function)로 승격시키는 역할을 해주며, 이렇게 변화되는 것을 람다표현식에서 eta-expansion이라고 부릅니다. (이타확장이라고 불러야 하는지, 에타확장이라고 불러야 하는지;;;)
다음과 같이, _
(place holder)를 이용해서, 우리가 직접 메소드를 함수로 변화 시킬 수 (eta-expansion) 있습니다.
val eMsg = mMsg _ //> eMsg : () => String = <function0>
글의 제일 처음에 설명했던 것 처럼, eta-expansion이 발생하는 부분에서 스칼라 파일을 컴파일 해 보면 각각 익명 클래스들이 생성되는 것을 확인 할 수 있습니다.
함수와 메소드는 분명 구분히 되지만, eta-expansion 때문에 메소드를 좀 더 함수처럼 사용할 수 있습니다. 스칼라가 어렵게 느껴지는 이유중 하나가, 이런 차이 때문에 발생하는 다양한 표현이 존재하기 때문이 아닌가 합니다.
무슨일이 있어도 안 거르려고 했지만... 오늘 이후로 딱 일주일(5회)만 쉬겠습니다. 제가 하루에 잠을 8시간은 꼬박꼬박 자야 머리가 돌아가는데, 추정을 잘못했던 프로젝트 마감이 겹친 관계로 최근 수면이 급격히 부족해지고, 업무에 영향도 미치고, 글도 자꾸 수정하게 되네요...
--웹에서 이 토론을 보려면 https://groups.google.com/d/msgid/scala-korea/CAD5fO2roK1Nipc3FRa5Sx8nnnZw1gomw9wZ5K_uKKpq8eYCbkw%40mail.gmail.com 을(를) 방문하세요.
Google 그룹스 '라 스칼라 코딩단' 그룹에 가입했으므로 본 메일이 전송되었습니다.
이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 scala-korea...@googlegroups.com에 이메일을 보내세요.
이 그룹에 게시하려면 scala...@googlegroups.com(으)로 이메일을 보내세요.
무슨일이 있어도 안 거르려고 했지만... 오늘 이후로 딱 일주일(5회)만 쉬겠습니다. 제가 하루에 잠을 8시간은 꼬박꼬박 자야 머리가 돌아가는데, 추정을 잘못했던 프로젝트 마감이 겹친 관계로 최근 수면이 급격히 부족해지고, 업무에 영향도 미치고, 글도 자꾸 수정하게 되네요...
--
Google 그룹스 '라 스칼라 코딩단' 그룹에 가입했으므로 본 메일이 전송되었습니다.
이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 scala-korea...@googlegroups.com에 이메일을 보내세요.
이 그룹에 게시하려면 scala...@googlegroups.com(으)로 이메일을 보내세요.
무슨일이 있어도 안 거르려고 했지만... 오늘 이후로 딱 일주일(5회)만 쉬겠습니다. 제가 하루에 잠을 8시간은 꼬박꼬박 자야 머리가 돌아가는데, 추정을 잘못했던 프로젝트 마감이 겹친 관계로 최근 수면이 급격히 부족해지고, 업무에 영향도 미치고, 글도 자꾸 수정하게 되네요...
--
Google 그룹스 '라 스칼라 코딩단' 그룹에 가입했으므로 본 메일이 전송되었습니다.
이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 scala-korea...@googlegroups.com에 이메일을 보내세요.
이 그룹에 게시하려면 scala...@googlegroups.com(으)로 이메일을 보내세요.