By-Name Parameters
Scala에서는 by-name parameters라는 유용한 feature를 제공합니다. 간단하게 어떤 용도로 사용할 수 있는지 알아봅시다.
ppassa님 블로그에 적힌 2.1 Loan Pattern을 보면 다음과 같은 Scala 코드를 볼 수 있습니다.
withFile(filename) {
line => storeToDb(line)
}
Loan Pattern을 언급하려는 것은 아니지만 다양한 feature를 비교 설명하기위해 언급합니다. java에서는 볼 수 없는 syntax인데 curly braces ({}) 안에 “line =>” 이라는 독특한 구문을 제외하면 if 또는 while 같은 built-in control structures (Programming in Scala Ch.9 참조) 비슷해 보입니다.
그럼 더 유사한 형태로 구현하고 싶다면 by-name parameters를 사용하면 됩니다. 예를 들면 다음과 같이 사용할 수 있습니다.
def loop(condition: => Boolean)(behaviour: => Unit) {
if (condition) {
behaviour; loop(condition)(behaviour)
}
}
var stop = false
loop(!stop) {
println("Running!")
}
loop라는 메서드를 보면 요상한 signature를 볼 수 있습니다. 보통의 function literals와 비슷해 보이지만 조금 다릅니다.
// Function Declations Syntax에서 조금 추려보면 다음과 같습니다.
Param ::= {Annotation} id [':' ParamType] ['=' Expr]
ParamType ::= Type | '=>' Type | Type '*'
// 이지만 By-name parameters는 ParamType이 아래와 같이 사용합니다.
ParamType ::= '=>' Type
쉽게 쓰면 아래와 같이 Parameter Type이 ‘=>’로 시작하면 됩니다.
def methodName(parameterName := Type)
특징은 다음과 같습니다. call-by-name으로 evaluate됩니다. 간단하게 말하면 “바로 evaluate되는 것이 아닌 내부에서 사용할 때 evaluate된다.”로 이해해도 될 것 같습니다.
이 특징을 이용하면 재밌는 구현이 가능해집니다.
def displayLazy(rows: => Seq[Int]) {
println("Lazy Loading")
rows map { println _ }
}
def displayEager(rows: Seq[Int]) {
println("Eager Loading")
rows map { println _ }
}
displayLazy({println("loading"); List(1,2,3,4)})
displayEager({println("loading"); List(1,2,3,4)})
위의 코드가 어떤 결과로 출력될지 메서드명을 보면 예측할 수 있습니다. 한 끗 차이로 다른 결과가 나올 수 있습니다.
scala> displayLazy({println("loading"); List(1,2,3,4)})
Lazy Loading
loading
1
2
3
4
scala> displayEager({println("loading"); List(1,2,3,4)})
loading
Eager Loading
1
2
3
4
def displayLazy2(rows: () => Seq[Int]) {
println("Lazy Loading")
rows() map { println _ }
}
displayLazy2({ () => println("loading"); List(1,2,3,4)})
Repeated Parameters
Repeated Parameters는 아주 간단하게 언급하고 넘어갈께요.
Scala에서는 마지막 parameter에대해서 repeated parameters로 선언할 수 있습니다.
def display(lines: String*) = lines map { println _ }
위에도 언급했듯이 마지막 parameter만 Type* 를 사용할 수 있습니다.
ParamType ::= Type '*'
또한 Array나 List같은 Seq타입을 인자로 보내면 바로 사용할 수 없고 사용하게 되면 에러와 마주하게 됩니다. (type mismatch 납니다.) 그 땐 다음과 같은 형태로 활용할 수 있습니다.
var messages = List("Hello", "World", "Scala") display(messages: _*)
Conclusions
이번 포스트도 성의가 없지만 이해해주세요. 요약하면 다음과 같습니다.
- by-name parameters의 경우는 참조될 때 evaluate됩니다.
- by-name parameters의 특징을 잘 살리면 코드를 많이 줄일 수 있습니다.
- by-name parameters 또한 이전 포스트에 작성한 Named Parameters, Default Arguments 모두 사용 가능합니다.
- repeated parameters는 named parameters는 사용할 수 있지만 Default Arguments는 사용할 수 없습니다.
다음에는 placeholder syntax 또는 pattern matching 같은 요상한 걸 정리해봐야겠습니다. ㅋㅋ
Tagged: by-name parameters, call-by-name, default arguments, named-parameters, repeat parameters
제가 아직 Scala 책을 제대로 독파하지 못해서 모르는 것이 많다는 걸 다시 한번 깨닫게 되네요.
Scala라는 언어 참 파워풀하네요. 아.. 이제 다시는 Java는 못쓰게 될 것 같습니다. ^^;;;
좋은 글 감사합니다.
Scala는 알면 알수록 매력적인 것 같아요. 현재는 직접 짜는 일은 없어서 잘 몰라요. 1~2달은 아주 깊게 파야할 것 같아요.