본문으둜 κ±΄λ„ˆλ›°κΈ°

🍭 Chapter 2: 슀칼라둜 ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ° μ‹œμž‘ν•˜κΈ°

πŸŽƒ 슀칼라 μ–Έμ–΄μ˜ μ†Œκ°œ: 예제 ν•˜λ‚˜β€‹

이번 절의 λͺ©ν‘œλŠ” 슀칼라 언어와 κ·Έ ꡬ문(문법)을 μ†Œκ°œν•˜λŠ” 것일 뿐이닀.

// 이것은 주석!
/* 이것도 주석 */
/** λ¬Έμ„œν™” 주석 */
object MyModule { // 단일 객체의 μ„ μ–Έ ν΄λž˜μŠ€μ™€ 클래슀의 μœ μΌν•œ μΈμŠ€ν„΄μŠ€λ₯Ό λ™μ‹œμ— μ„ μ–Έν•œλ‹€.
def abs(n: Int): Int =
// λ§Œμ•½ n이 0보닀 μž‘μœΌλ©΄ n의 λΆ€μ •(λΆ€ν˜Έκ°€ λ°˜λŒ€)을 λŒλ €μ€€λ‹€.
if (n < 0) -n
else n

// μ „μš©(private) λ©”μ„œλ“œλŠ” 였직 MyModule의 λ‹€λ₯Έ λ©€λ²„λ“€λ§Œ ν˜ΈμΆœν•  수 μžˆλ‹€.
private def formatAbs(x: Int) = {
// 수치λ₯Ό μœ„ν•œ μžλ¦¬ν‘œ %dκ°€ 두 개 μžˆλŠ” λ¬Έμžμ—΄.
val msg = "The absolute value of %d is %d"
// λ¬Έμžμ—΄ 두 %d μžλ¦¬ν‘œλ₯Ό 각각 x와 abs(x)둜 μΉ˜ν™˜ν•œλ‹€.
msg.format(x, abs(x))
}

// Unit은 Javaλ‚˜ C 같은 μ–Έμ–΄μ˜ void와 같은 λͺ©μ μœΌλ‘œ 쓰인닀.
def main(args: Array[String]): Unit =
println(formatAbs(-42))
}

슀칼라 μ½”λ“œλŠ” λ°˜λ“œμ‹œ object둜 μ„ μ–Έλ˜λŠ” κ°μ²΄λ‚˜ class둜 μ„ μ–Έλ˜λŠ” 클래슀 μ•ˆμ— λ“€μ–΄κ°€μ•Ό ν•˜λŠ”λ°, μ—¬κΈ°μ„œλŠ” λ‘˜ 쀑 더 κ°„λ‹¨ν•œ objectλ₯Ό μ‚¬μš©ν•œλ‹€.

abs λ©”μ„œλ“œλŠ” μ •μˆ˜ ν•˜λ‚˜λ₯Ό λ°›μ•„μ„œ κ·Έ μ ˆλŒ“κ°’μ„ λŒλ €μ£ΌλŠ” 순수 ν•¨μˆ˜μ΄λ‹€.

def abs(n: Int): Int =
if (n < 0) -n
else n

λ©”μ„œλ“œ 자체의 본문은 λ“±ν˜Έ(=) λ‹€μŒμ— μ˜¨λ‹€. ν”νžˆ, λ©”μ„œλ“œ μ„ μ–Έμ—μ„œ λ“±ν˜Έ μ™Όμͺ½μ•  μ•˜λŠ” 것을 μ’Œλ³€ λ˜λŠ” μ„œλͺ…이라고 λΆ€λ₯΄κ³  λ“±ν˜Έ 였λ₯Έμͺ½μ˜ μ½”λ“œλ₯Ό μš°λ³€ λ˜λŠ” μ •μ˜λΌκ³  λΆ€λ₯Έλ‹€. λ©”μ„œλ“œμ˜ μ •μ˜μ— λͺ…μ‹œμ μΈ return ν‚€μ›Œλ“œκ°€ μ—†λ‹€. κ·Έλƒ₯ μš°λ³€μ˜ 평가 κ²°κ³Όκ°€ 곧 λ©”μ„œλ“œμ˜ λ°˜ν™˜κ°’μ΄λ‹€.

formatAbs λ©”μ„œλ“œ μ—­μ‹œ 순수 ν•¨μˆ˜μ΄λ‹€.

private def formatAbs(x: Int) = {
val msg = "The absolute value of %d is %d"
msg.format(x, abs(x))
}

이 λ©”μ„œλ“œλŠ” private둜 μ„ μ–Έλ˜μ–΄ μžˆλ‹€. μ΄λŠ” 이 λ©”μ„œλ“œλ₯Ό MyModule 객체의 μ˜€λΆ€μ—μ„œλŠ” ν˜ΈμΆœν•  수 μ—†μŒμ„ λœ»ν•œλ‹€. 이 ν•¨μˆ˜λŠ” Int ν•˜λ‚˜λ₯Ό λ°›κ³  String ν•˜λ‚˜λ₯Ό λŒλ €μ£ΌλŠ”λ‹€, λ°˜ν™˜ ν˜•μ‹μ΄ μ„ μ–Έλ˜μ–΄ μžˆμ§€ μ•Šλ‹€. μŠ€μΉΌλΌκ°€ λ©”μ„œλ“œμ˜ λ°˜ν™˜ ν˜•μ‹μ„ μΆ”λ‘ ν•  수 μžˆλŠ” κ²½μš°κ°€ 많으며, 그럴 λ•Œμ—λŠ” λ°˜ν™˜ ν˜•μ‹μ„ μƒλž΅ν•  수 μžˆλ‹€. κ·ΈλŸ¬λ‚˜ λ‹€λ₯Έ μ‚¬λžŒμ΄ μ‚¬μš©ν•  λ©”μ„œλ“œμ—μ„œλŠ” λ°˜ν™˜ ν˜•μ‹μ„ λͺ…μ‹œμ μœΌλ‘œ 지정해 μ£ΌλŠ” 것이 λŒ€μ²΄λ‘œ 쒋은 μŠ€νƒ€μΌλ‘œ κ°„μ£Όλœλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ, main λ©”μ„œλ“œλŠ” 순수 ν•¨μˆ˜μ  핡심뢀λ₯Ό ν˜ΈμΆœν•˜κ³  κ·Έ κ²°κ³Όλ₯Ό μ½˜μ†”μ— 좜λ ₯ν•˜λŠ” μ™ΈλΆ€ 계측(shell)이닀. λΆ€μˆ˜ νš¨κ³Όκ°€ λ°œμƒν•¨μ„ κ°•μ‘°ν•˜κΈ° μœ„ν•΄, 이런 λ©”μ„œλ“œλ₯Ό 절차(procedure) λ˜λŠ” 뢈순 ν•¨μˆ˜(impure function)라고 λΆ€λ₯΄κΈ°λ„ ν•œλ‹€.

def main(args: Array[String]): Unit = 
println(formatAbs(-42))

mainμ΄λΌλŠ” 이름은 νŠΉλ³„ν•˜λ‹€. ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰ν•  λ•Œ μŠ€μΉΌλΌκ°€ νŠΉμ •ν•œ μ„œλͺ…을 가진 mainμ΄λΌλŠ” μ΄λ¦„μ˜ λ©”μ„œλ“œλ₯Ό μ°ΎκΈ° λ•Œλ¬Έμ΄λ‹€. μ’€ 더 ꡬ체적으둜, main λ©”μ„œλ“œλŠ” λ°˜λ“œμ‹œ Stringλ“€μ˜ Arrayλ₯Ό 인수둜 λ°›μ•„μ•Ό ν•˜λ©°, λ°˜ν™˜ ν˜•μ‹μ€ λ°˜λ“œμ‹œ Unit이어야 ν•œλ‹€.

μŠ€μΉΌλΌμ—μ„œ λͺ¨λ“  λ©”μ„œλ“œλŠ” ν­μ£Όν•˜μ§€ μ•ŠλŠ” ν•œ μ–΄λ–€ 값을 λŒλ €μ€€λ‹€. 그런데 main은 λ”±νžˆ 의미 μžˆλŠ” 값을 λŒλ €μ£Όμ§€ μ•ŠλŠ”λ‹€. νŠΉλ³„ν•œ ν˜•μ‹μΈ Unit은 그런 γ„΄λ©”μ„œλ“œμ˜ λ°˜ν™˜ ν˜•μ‹μœΌλ‘œ 쓰인닀. 일반적으둜, λ°˜ν™˜ ν˜•μ‹μ΄ Unitμ΄λΌλŠ” 것은 κ·Έ λ©”μ„œλ“œμ— λΆ€μˆ˜ νš¨κ³Όκ°€ μ‘΄μž¬ν•¨μ„ μ•”μ‹œν•œλ‹€.

πŸŽƒ ν”„λ‘œκ·Έλž¨μ˜ 싀행​

보톡은 슀칼라용 λΉŒλ“œ 도ꡬ인 sbtλ₯Ό μ΄μš©ν•˜κ±°λ‚˜ IntelliJλ‚˜ Eclipse 같은 IDEλ₯Ό μ΄μš©ν•΄ 슀칼라 μ½”λ“œλ₯Ό κ΅¬μΆ•ν•˜κ³  μ‹€ν–‰ν•œλ‹€.

이 슀칼라 ν”„λ‘œκ·Έλž¨(MyModule)을 μ‹€ν–‰ν•˜λŠ” κ°€μž₯ κ°„λ‹¨ν•œ 방법은 λͺ…λ Ήμ€„μ—μ„œ 직접 슀칼라 μ»΄νŒŒμΌλŸ¬λŠ” λΆˆλŸ¬μ˜€λŠ” 것이닀. μš°μ„  μ•žμ˜ ν”„λ‘œκ·Έλž¨μ„ MyModule.scala와 같은 μ΄λ¦„μ˜ 파일둜 μ €μž₯ν•˜κ³ , λ‹€μŒκ³Ό 같이 scalac 컴파일러λ₯Ό μ΄μš©ν•΄μ„œ κ·Έ ν”„λ‘œκ·Έλž¨μ„ Java λ°”μ΄νŠΈμ½”λ“œλ‘œ 컴파일 ν•œλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ ν™•μž₯μžκ°€ .class인 νŒŒμΌλ“€μ΄ 생긴닀. 이 νŒŒμΌλ“€μ—λŠ” Java 가상 기게(JVM)둜 μ‹€ν–‰ν•  수 μžˆλŠ” 컴파일된 μ½”λ“œκ°€ λ“€μ–΄ μžˆλ‹€. κ·Έ μ½”λ“œλ₯Ό λͺ…령쀄 도ꡬ scalaλ₯Ό μ΄μš©ν•΄μ„œ 직접 μ‹€ν–‰ν•  수 μžˆλ‹€.

또 λ‹€λ₯Έ λ°©λ²•μœΌλ‘œ, 슀칼라 ν•΄μ„κΈ°μ˜ λŒ€ν™”μ‹ λͺ¨λ“œ, 즉 REPL(read-evaluate-print loop)을 μ΄μš©ν•΄λ„ λœλ‹€. REPL은 λ…μžκ°€ 슀칼라 ν”„λ‘œκ·Έλž˜λ¨Έλ‘œμ„œ 자주 μ‚¬μš©ν•˜κ²Œ 될 λ„κ΅¬μ΄λ―€λ‘œ, REPL의 μ—¬λŸ¬ 가지 κΈ°λŠ₯에 μ΅μˆ™ν•˜μ§€λ©΄ 도움이 될 것이닀.

πŸŽƒ λͺ¨λ“ˆ, 객체, 이름곡간​

MyModule은 absκ°€ μ†ν•œ 이름곡간(namespace)이닀. λͺ‡ 가지 세뢀적인 사항을 μƒλž΅ν•  λ•Œ, 슀칼라의 λͺ¨λ“  값은 μ†Œμœ„ 객체(object)이며, 각각의 κ°μ²΄λŠ” 0개 λ˜λŠ” ν•˜λ‚˜ μ΄μƒμ˜ 멀버(member)λ₯Ό κ°€μ§ˆ 수 μžˆλ‹€. μžμ‹ μ˜ λ©€λ²„λ“€μ—κ²Œ 이름곡간을 μ œκ³΅ν•˜λŠ” 것이 주된 λͺ©μ μΈ 객체λ₯Ό ν”νžˆ λͺ¨λ“ˆ(module)이라고 λΆ€λ₯Έλ‹€. λ©€λ²„λŠ” def ν‚€μ›Œλ“œλ‘œ μ„ μ–Έλœ λ©”μ„œλ“œμΌ μˆ˜λ„ 있고 valμ΄λ‚˜ object둜 μ„ μ–Έλœ 또 λ‹€λ₯Έ 객체일 μˆ˜λ„ μžˆλ‹€.

μŠ€μΉΌλΌμ—λŠ” μ—°μ‚°μžλΌλŠ” νŠΉλ³„ν•œ κ°œλ…μ΄ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€. μŠ€μΉΌλΌμ—μ„œ +λŠ” κ·Έλƒ₯ μœ νš¨ν•œ λ©”μ„œλ“œ 이름일 뿐이며, 인수 ν•˜λ‚˜μΈ λ©”μ„œλ“œλŠ” κ·Έ μ–΄λ–€ 것이라도 λ§ˆμΉ¨ν‘œμ™€ κ΄„ν˜Έλ₯Ό μƒλž΅ν•œ μ€‘μœ„ ν‘œκΈ°λ²•μœΌλ‘œ ν˜ΈμΆœν•  수 μžˆλ‹€. 예λ₯Ό λ“€μ–΄ MyModule.abs(42) λŒ€μ‹  MyModule abs 42라고 해도 같은 κ²°κ³Όκ°€ λœλ‹€. 그러면 객체 이름을 μƒλž΅ν•  수 μžˆλ‹€.

그리고 λ‹€μŒκ³Ό 같이 밑쀄 ν‘œκΈ°λ²•μ„ μ΄μš©ν•˜λ©΄ 객체의 λͺ¨λ“  λΉ„μ „μš© 멀버λ₯Ό λ²•μœ„μ— λ„μž…ν•  수 μžˆλ‹€.

import MyModule._

πŸŽƒ κ³ μ°¨ ν•¨μˆ˜: ν•¨μˆ˜λ₯Ό ν•¨μˆ˜μ— 전달​

μ •μˆ˜λ‚˜ λ¬Έμžμ—΄, λͺ©λ‘ 같은 λ‹€λ₯Έ ν˜•μ‹μ˜ κ°’μ²˜λŸΌ ν•¨μˆ˜λ„ λ³€μˆ˜μ— λ°°μ •ν•˜κ±°λ‚˜ μžλ£Œκ΅¬μ‘°μ— μ €μž₯ν•˜κ±°λ‚˜ μΈμˆ˜λ‘œμ„œ ν•¨μˆ˜μ— λ„˜κ²¨μ€„ 수 μžˆλ‹€.

μˆœμˆ˜ν•œ ν•¨μˆ˜μ  ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•  λ•ŒλŠ” λ‹€λ₯Έ ν•¨μˆ˜λ₯Ό 인수둜 λ°›λŠ” ν•¨μˆ˜λ₯Ό μž‘μ„±ν•˜λŠ” 것이 μœ μš©ν•œ κ²½μš°κ°€ λ§Žλ‹€. 그런 ν•¨μˆ˜λ₯Ό κ³ μ°¨ ν•¨μˆ˜(higher-order function, HOF)라고 λΆ€λ₯Έλ‹€.

🎈 잠깐 곁가지: ν•¨μˆ˜μ μœΌλ‘œ 루프 μž‘μ„±ν•˜κΈ°β€‹

κ³„μŠΉμ„ κ΅¬ν•˜λŠ” factorial ν•¨μˆ˜λΆ€ν„° μž‘μ„±ν•΄λ³΄μž.

def factorial(n: Int): Int = {
def go(n: Int, acc: Int): Int =
if (n <= 0) acc
else go(n - 1, n*acc)

go(n, 1)
}

루프λ₯Ό ν•¨μˆ˜μ μœΌλ‘œ μž‘μ„±ν•˜λŠ” 방법은 λ°”λ‘œ μž¬κ·€ ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜λŠ” 것이닀. μŠ€μΉΌλΌλŠ” 이런 μ’…λ₯˜μ˜ 자기 μž¬κ·€(self-recursion)λ₯Ό κ²€μΆœν•΄μ„œ, μž¬κ·€ 호좜이 꼬리 μœ„μΉ˜(tail position)μ—μ„œ μΌμ–΄λ‚œλ‹€λ©΄ while 루프λ₯Ό μ‚¬μš©ν–ˆμ„ λ•Œμ™€ 같은 μ’…λ£Œμ˜ λ°”μ΄νŠΈμ½”λ“œλ‘œ μ»΄νŒŒμΌν•œλ‹€. 핡심은, μž¬κ·€ 호좜의 λ°˜ν™˜ μ΄μš°μ— νŠΉλ³„νžˆ 더 ν•˜λŠ” 일이 μ—†λ‹€λ©΄ 이런 μ’…λ₯˜μ˜ μ΅œμ ν™”(꼬리 호좜 제거라고 λΆ€λ₯Έλ‹€)κ°€ μ μš©λœλ‹€λŠ” 것이닀.

슀칼라의 꼬리 호좜
ν˜ΈμΆœμžκ°€ μž¬κ·€ 호좜의 κ²°κ³Όλ₯Ό κ·Έλž˜λ„ λŒλ €μ£ΌλŠ” 것 μ™Έμ—λŠ” 아무 일도 ν•˜μ§€ μ•Šμ„ λ•Œ, 그런 ν˜ΈμΆœμ„ 꼬리 μœ„μΉ˜μ—μ„œμ˜ 호좜, μ€„μ—¬μ„œ 꼬리 호좜이라고 λ§ν•œλ‹€. λ§Œμ•½μ— 1 + go(n - 1, n * acc) μž¬κ·€ 호좜 인 경우 더 이상 꼬리 호좜이 μ•„λ‹ˆλ‹€. goμ˜κ²°κ³Όμ— λŒ€ν•΄ λ‹€λ₯Έ μ–΄λ–€ 일(1을 λ”ν•˜λŠ” 것)을 μˆ˜ν–‰ν•΄μ•Ό ν•˜κΈ° 떄문이닀.
ν•œ ν•¨μˆ˜κ°€ μˆ˜ν–‰ν•˜λŠ” λͺ¨λ“  μž¬κ·€ 호좜이 꼬리 호좜이면 μŠ€μΉΌλΌλŠ” ν•΄λ‹Ή μž¬κ·€λ₯Ό 맀 λ°˜λ³΅λ§ˆλ‹€ 호좜 μŠ€νƒμ„ μ†ŒλΉ„ν•˜μ§€ μ•ŠλŠ” 반볡 루프 ν˜•νƒœλ‘œ 컴파일 ν•œλ‹€. κΈ°λ³Έ μ„€μ •μ—μ„œ 슀칼라 μ»΄νŒŒμΌλŸ¬λŠ” 꼬리 호좜 제거의 성곡 μ—¬λΆ€λ₯Ό μ•Œλ €μ£Όμ§€ μ•Šμ§€λ§Œ, μžμ‹ μ΄ μž‘μ„±ν•œ μž¬κ·€ ν•¨μˆ˜μ— λŒ€ν•΄ 꼬리 호좜이 μ‹€μ œλ‘œ μ œκ±°λ˜μ—ˆλŠ”μ§€ 확인할 ν•„μš”κ°€ μžˆλ‹€λ©΄ tailrec μ£Όν•΄(http://mng.bz/bWT5)λ₯Ό μž¬κ·€ ν•¨μˆ˜μ— μ μš©ν•˜λ©΄ λœλ‹€.

🎈 첫 번째 κ³ μ°¨ ν•¨μˆ˜ μž‘μ„±β€‹

factorial ν•¨μˆ˜λ₯Ό λ§Œλ“€μ—ˆμœΌλ‹ˆ, 이제 이 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ„λ‘ 예제 ν”„λ‘œκ·Έλž¨μ„ μˆ˜μ •ν•΄ 보자.

object MyModule {
def abs(n: Int): Int =
if (n < 0) -n
else n

def factorial(n: Int): Int = {
def go(n: Int, acc: Int): Int =
if (n <= 0) acc
else go(n - 1, n*acc)

go(n, 1)
}

private def formatAbs(x: Int) = {
val msg = "The absolute value of %d is %d"
msg.format(x, abs(x))
}

private def formatFactorial(n: Int) = {
val msg = "The factorial of %d is $d."
msg.format(n, factorial(n))
}

def main(args: Array[String]): Unit = {
println(formatAbs(-42))
println(formatFactorial(7))
}
}

두 ν•¨μˆ˜ formatAbs와 formatFactorial은 거의 λ™μΌν•˜λ‹€ 이 λ‘˜μ„ λ‹€μŒμ²˜λŸΌ μΈμˆ˜λ“€μ— μ μš©ν•  ν•¨μˆ˜λ₯Ό 인수둜 λ°›λŠ” ν•˜λ‚˜μ˜ ν•¨μˆ˜ formatResult둜 μΌλ°˜ν™”ν•˜λ©΄ μ–΄λ–¨κΉŒ?

def formatResult(name: String, n: Int, f: Int => Int) = {
val msg = "The %s of %d is %d."
msg.format(name, n, f(n))
}

이 formatResult ν•¨μˆ˜λŠ” fλΌλŠ” λ‹€λ₯Έ ν•¨μˆ˜λ₯Ό λ°›λŠ” ν•˜λ‚˜μ˜ κ³ μ°¨ ν•¨μˆ˜λ‹€. λ‹€λ₯Έ μΈμˆ˜λ“€μ²˜λŸΌ f에도 ν˜•μ‹μ„ μ§€μ •ν–ˆλ‹€. 이 인수의 ν˜•μ‹μ€ Int => Int인데, fκ°€ μ •μˆ˜ 인수 ν•˜λ‚˜λ₯Ό λ°›κ³  μ •μˆ˜ ν•˜λ‚˜λ₯Ό λŒλ €μ£ΌλΌλŠ” λœ»μ΄λ‹€.

formatResult("absolute value", -42, abs)
formatResult("factorial", 7, factorial)

πŸŽƒ λ‹€ν˜•μ  ν•¨μˆ˜: ν˜•μ‹μ— λŒ€ν•œ 좔상​

μ§€κΈˆκΉŒμ§€ μ •μ˜ν•œ ν•¨μˆ˜λ“€μ€ λͺ¨λ‘ λ‹¨ν˜•μ  ν•¨μˆ˜(monomorphic function), 즉 ν•œ ν˜•μ‹μ˜ μžλ£Œμ—λ§Œ μž‘μš©ν•˜λŠ” ν•¨μˆ˜μ˜€λ‹€. 예λ₯Ό λ“€μ–΄ abs와 factorial은 Int ν˜•μ‹μ˜ μΈμˆ˜μ—λ§Œ νŠΉν™”λ˜μ–΄ 있으며, κ³ μ°¨ ν•¨μˆ˜ formatResult μ—­μ‹œ Int ν˜•μ‹μ˜ 인수λ₯Ό λ°›λŠ” ν•¨μˆ˜μ—λ§Œ μž‘μš©ν•˜λ„λ‘ κ³ μ •λ˜μ–΄ μžˆλ‹€. 그런데 μž„μ˜μ˜ ν˜•μ‹μ— λŒ€ν•΄ μž‘λ™ν•˜λŠ” μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ•Ό ν•˜λŠ” κ²½μš°λ„ 많이 생긴닀. 그런 ν•¨μˆ˜λ₯Ό λ‹€ν˜•μ  ν•¨μˆ˜(polymorphic function)라고 λΆ€λ₯Έλ‹€.

🎈 λ‹€ν˜•μ  ν•¨μˆ˜μ˜ μ˜ˆβ€‹

ꡬ쑰가 λΉ„μŠ·ν•œ λ‹¨ν˜•μ  ν•¨μˆ˜ μ—¬λŸ¬ 개λ₯Ό κ΄€μ°°ν•˜λ‹€ 보면 λ‹€ν˜•μ  ν•¨μˆ˜λ₯Ό λ°œκ²¬ν•˜λŠ” κ²½μš°κ°€ λ§Žλ‹€.

// λ°°μ—΄μ—μ„œ λ¬Έμžμ—΄μ„ μ°ΎλŠ” λ‹¨ν˜•μ  ν•¨μˆ˜
def findFirst(ss: Array[String], key: String): Int = {
@annotation.tailrec
def loop(n: Int): Int =
if (n >= ss.length) -1
else if (ss(n) == key) n
else loop(n + 1)
}

λ‹€μŒμ€ μž„μ˜μ˜ ν˜•μ‹ A에 λŒ€ν•΄ νŠΉμ • A 값을 μ κ²€ν•˜λŠ” ν•¨μˆ˜λ₯Ό 인수둜 λ°›μŒμœΌλ‘œμ¨ findFirstλ₯Ό μ’€ 더 μΌλ°˜ν™”ν•œ 것이닀.

def findFirst[A](as: Array[A], p: A => Boolean): Int = {
@annotation.tailrec
def loop(n: Int): Int =
if (n >= as.length) - 1
// ν•¨μˆ˜ pκ°€ ν˜„μž¬ μš”μ†Œμ™€ λΆ€ν•©ν•œλ‹€λ©΄ μ›ν•˜λŠ” μš”μ†Œλ₯Ό μ°ΎλŠ” κ²ƒμ΄λ―€λ‘œ λ°°μ—΄ μ•ˆμ—μ„œ ν•΄λ‹Ή 색인을 λŒλ €μ€€λ‹€.
else if (p(as(n))) n
else loop(n + 1)

loop(0)
}

μ΄λŠ” 일반적 ν•¨μˆ˜(generic function)라고도 λΆ€λ₯΄λŠ” λ‹€ν˜•μ  ν•¨μˆ˜μ˜ ν•œ μ˜ˆμ΄λ‹€. 이 ν•¨μˆ˜λŠ” ν˜•μ‹μ— λŒ€ν•œ 좔상(abstracting over the type)을 λ°°μ—΄κ³Ό λ°°μ—΄ μ•ˆμ˜ ν•œ μš”μ†Œλ₯Ό κ²€μƒ‰ν•˜λŠ” ν•¨μˆ˜μ— μ μš©ν•œ 결과이닀. λ‹€ν˜•μ  ν•¨μˆ˜λ₯Ό ν•˜λ‚˜μ˜ λ©”μ„œλ“œλ‘œμ„œ μž‘μ„±ν•  λ•Œμ—λŠ” μ‰Όν‘œλ‘œ κ΅¬λΆ„λœ ν˜•μ‹ λ§€κ°œλ³€μˆ˜(type parameter)λ“€μ˜ λͺ©λ‘μ„ λŒ€κ΄„ν˜Έλ‘œ 감싸고(μ§€κΈˆ μ˜ˆμ—μ„œλŠ” ν•˜λ‚˜μ˜ [A]), κ·Έλ‹€μŒμ— ν•¨μˆ˜μ˜ 이름을 μ§€μ •ν•œλ‹€(μ§€κΈˆμ˜ μ˜ˆμ—μ„œλŠ” findFirst). ν˜•μ‹ λ§€κ°λ³€μˆ˜ μ΄λ¦„μœΌλ‘œλŠ” μ–΄λ–€ 것이라도 μ‚¬μš©ν•  수 μžˆλ‹€. κ·ΈλŸ¬λ‚˜ 보톡은 [A, B, C] 같은 짧은 ν•œ κΈ€μžμ§œλ¦¬ λŒ€λ¬Έμž ν˜•μ‹ λ§€κ°œλ³€μˆ˜ 이름듀을 μ‚¬μš©ν•˜λŠ” 것이 관둀이닀.

ν˜•μ‹ λ§€κ°œλ³€μˆ˜ λͺ©λ‘μ€ ν˜•μ‹ μ„œλͺ…μ˜ λ‚˜λ¨Έμ§€ λΆ€λΆ„μ—μ„œ μ°Έκ³ ν•  수 μžˆλŠ” ν˜•μ‹ λ³€μˆ˜(type variable)듀을 λ„μž…ν•œλ‹€. findFirstμ—μ„œ ν˜•μ‹ λ³€μˆ˜ AλŠ” 두 κ³³μ—μ„œ μ°Έμ‘°λœλ‹€. λ°°μ—΄μ˜ μ›μ†Œλ“€μ€ ν˜•μ‹μ΄ λ°˜λ“œμ‹œ A이어야 ν•˜λ©°, ν•¨μˆ˜ pλŠ” λ°˜λ“œμ‹œ A ν˜•μ‹μ˜ 값을 λ°›μ•„μ•Ό ν•œλ‹€. ν˜•μ‹ μ„œλͺ…μ˜ 두 μž₯μ†Œμ—μ„œ λ™μΌν•œ ν˜•μ‹ λ³€μˆ˜λ₯Ό μ°Έμ‘°ν•œλ‹€λŠ” 사싀은 ν•΄λ‹Ή 두 인수의 ν˜•μ‹μ΄ 동일해야 함을 μ˜λ―Έν•˜λ©°, μ»΄νŒŒμΌλŸ¬λŠ” λͺ¨λ“  findFirst 호좜 μž₯μ†Œμ—μ„œ 이 사싀을 κ°•μ œν•œλ‹€.

🎈 읡λͺ… ν•¨μˆ˜λ‘œ κ³ μ°¨ ν•¨μˆ˜ ν˜ΈμΆœβ€‹

κ³ μ°¨ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•  λ•Œ, 기쑴의 이름 뢙은 ν•¨μˆ˜λ₯Ό 인수둜 μ§€μ •ν•΄μ„œ ν˜ΈμΆœν•˜λŠ” 것이 μ•„λ‹ˆλΌ 읡λͺ… ν•¨μˆ˜(anonymous function) λ˜λŠ” ν•¨μˆ˜ λ¦¬ν„°λŸ΄(function literal)을 μ§€μ •ν•΄μ„œ ν˜ΈμΆœν•˜λŠ” 것이 νŽΈλ¦¬ν•œ κ²½μš°κ°€ λ§Žλ‹€.

findFirst(Array(7, 9, 13), (x: Int) => x == 9)

μœ„ μ˜ˆμ—μ—μ„œ λ°°μ—΄ λ¦¬ν„°λŸ΄μ€ μ •μˆ˜ μ„Έ 개λ₯Ό 담은 μƒˆ 배열을 μƒμ„±ν•˜κ³  new 같은 ν‚€μ›Œλ“œ 없이 배열을 생성할 수 μžˆλ‹€. (x: Int) => x == 9λΌλŠ” ꡬ문은 ν•¨μˆ˜ λ¦¬ν„°λŸ΄ λ˜λŠ” 읡λͺ… ν•¨μˆ˜μ΄λ‹€. 일반적으둜, ν•¨μˆ˜μ˜ μΈμˆ˜λ“€μ„ => ν™”μ‚΄ν‘œμ˜ μ’Œλ³€μ—μ„œ μ„ μ–Έλœλ‹€. μ„ μ–Έλœ μΈμˆ˜λŠ” ν™”μ‚΄ν‘œ 였λ₯Έμͺ½μ— μžˆλŠ” ν•¨μˆ˜ λ³Έλ¬Έμ—μ„œ μ‚¬μš©ν•  수 μžˆλ‹€.

(Int, Int) => Boolean = <function2>

πŸŽƒ ν˜•μ‹μ—μ„œ λ„μΆœλœ κ΅¬ν˜„β€‹

λ‹€ν˜•μ  ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•  λ•Œμ—λŠ” κ°€λŠ₯ν•œ κ΅¬ν˜„λ“€μ˜ 곡간이 크게 쀄어든닀. ν•¨μˆ˜κ°€ μ–΄λ–€ ν˜•μ‹ A에 λŒ€ν•΄ λ‹€ν˜•μ μ΄λ©΄, A에 λŒ€ν•΄μ„œλŠ” 였직 μΈμˆ˜λ“€λ‘œμ„œ ν•¨μˆ˜μ— μ „λ‹¬λœ μ—°μ‚°λ“€λ§Œ μˆ˜ν–‰ν•  수 μžˆλ‹€. 심지어, 주어진 λ‹€ν˜•μ  ν˜•μ‹μ— λŒ€ν•΄ 였직 단 ν•˜λ‚˜μ˜ κ΅¬ν˜„λ§Œ κ°€λŠ₯ν•΄μ§ˆ μ •λ„λ‘œ κ°€λŠ₯μ„±μ˜ 곡간이 μΆ•μ†Œλ˜λŠ” κ²½μš°λ„ μžˆλ‹€.

그럼 단 ν•œ 가지 λ°©μ‹μœΌλ‘œλ§Œ κ΅¬ν˜„ν•  수 μžˆλŠ” ν•¨μˆ˜ μ„œλͺ…μ˜ 예둜, λΆ€λΆ„ 적용(partial application)이라고 λΆ€λ₯΄λŠ” μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” κ³ μ°¨ ν•¨μˆ˜λ₯Ό μ‚΄νŽ΄λ³΄μž. partial1μ΄λΌλŠ” 이 ν•¨μˆ˜λŠ” κ°’ ν•˜λ‚˜μ™€ ν•¨μˆ˜(인수λ₯Ό 두 개 λ°›λŠ”) ν•˜λ‚˜λ₯Ό λ°›κ³  인수λ₯Ό ν•˜λ‚˜μΈ ν•¨μˆ˜λ₯Ό 결과둜 λŒλ €μ€€λ‹€.

def partial1[A, B, C](a: A, f: (A, B) => C): B => C

μ΄λŸ¬ν•œ κ³ μ°¨ ν•¨μˆ˜λ₯Ό μ–΄λ–»κ²Œ κ΅¬ν˜„ν•΄μ•Ό ν• κΉŒ? 컴파일 κ°€λŠ₯ν•œ κ΅¬ν˜„μ€ 단 ν•œ 가지이며, κ·Έ κ΅¬ν˜„μ€ ν•¨μˆ˜μ˜ ν˜•μ‹ μ„œλͺ…μ—μ„œ λ…Όλ¦¬μ μœΌλ‘œ λ„μΆœλœλ‹€. partial1의 λ°˜ν™˜ ν˜•μ‹μ€ B => Cμ΄λ―€λ‘œ, λ°˜λ“œμ‹œ 그런 ν˜•μ‹μ˜ ν•¨μˆ˜λ₯Ό λŒλ €μ£Όμ–΄μ•Ό ν•œλ‹€.

def partial1[A, B, C](a: A, f: (A, B) => C): B => C = 
(b: B) => ???

BλŠ” μ–΄λ””μ„œ λΉ„λ‘―λœ κ²ƒμΌκΉŒ?? 방금 μΆ”κ°€ν•œ μ½”λ“œλŠ” "B ν˜•μ‹μ˜ κ°’ bλ₯Ό λ°›λŠ” ν•¨μˆ˜λ₯Ό λŒλ €μ€€λ‹€"λΌλŠ” λœ»μ΄λ‹€. => ν™”μ‚΄ν‘œμ˜ μš°λ³€(μ§€κΈˆ λ¬ΌμŒν‘œλ“€)은 κ·Έ 읡λͺ… ν•¨μˆ˜μ˜ 본문이닀. partial1의 λ³Έλ¬Έμ—μ„œ a의 값을 μ°Έμ‘°ν•  수 μžˆλŠ” κ²ƒμ²˜λŸΌ. κ·Έ μš°λ³€μ—μ„œ κ°’ bλ₯Ό λ§ˆμŒλŒ€λ‘œ μ°Έμ‘°ν•  수 μžˆλ‹€.

λ‹€μŒμœΌλ‘œ, ν˜•μ‹ Bλ₯Ό λ°›λŠ” ν•¨μˆ˜κ°€ λŒλ €μ£ΌλŠ” 값은 μ–΄λ–€ 것이어야 ν• κΉŒ? ν˜•μ‹ μ„œλͺ…을 보면 κ·Έ κ°’μ˜ ν˜•μ‹μ΄ C이어야 함을 μ•Œ 수 μžˆλ‹€. 그리고 κ·ΈλŸ¬ν•œ ν˜•μ‹μ˜ 값을 μ–»λŠ” 방법은 단 ν•˜λ‚˜λΏμ΄λ‹€. μ„œλͺ…에 λ”°λ₯΄λ©΄ CλŠ” ν•¨μˆ˜ f의 λ°˜ν™˜ ν˜•μ‹μ΄λ‹€.

def partial1[A, B, C](a: A, f: (A, B) => C): B => C = 
(b: B) => f(a, b)

이제 μΈμˆ˜κ°€ 두 개인 ν•¨μˆ˜λ₯Ό λ°›μ•„μ„œ 그것을 λΆ€λΆ„μ μœΌλ‘œ μ μš©ν•˜λŠ” κ³ μ°¨ ν•¨μˆ˜κ°€ λ§Œλ“€μ–΄μ‘Œλ‹€. 즉, 만일 AλΌλŠ” ν˜•μ‹μ΄ 있고 A와 Bλ₯Ό λͺ¨λ‘ λ°›μ•„μ„œ Cλ₯Ό μ‚°μΆœν•˜λŠ” ν•¨μˆ˜κ°€ μžˆλ‹€λ©΄, Bλ§ŒμœΌλ‘œλ„ Cλ₯Ό μ‚°μΆœν•˜λŠ” ν•¨μˆ˜λ₯Ό 얻을 수 μžˆλŠ” 것이닀(AλŠ” 이미 μžˆμœΌλ―€λ‘œ).

μ—¬κΈ°μ„œ b에 λŒ€ν•œ ν˜•μ‹μ€ 지정할 ν•„μš”κ°€ μ—†μŒμ„ μ£Όλͺ©ν•˜μž. λ°˜ν™˜ ν˜•μ‹μ„ B => C둜 λͺ…μ‹œν–ˆμœΌλ―€λ‘œ, κ΅¬ν˜„μ—μ„œ κ·Έλƒ₯ b => f(a, b)라고 ν‘œκΈ°ν•΄λ„ μŠ€μΉΌλΌλŠ” λ¬Έλ§₯μ—μ„œ b의 ν˜•μ‹μ„ μΆ”λ‘ ν•  수 μžˆλ‹€. μΌλ°˜ν™”ν•˜μžλ©΄, μŠ€μΉΌλΌκ°€ μΆ”λ‘ ν•  수 μžˆμ„ λ•Œμ—λŠ” ν•¨μˆ˜ λ¦¬ν„°λŸ΄μ—μ„œ ν˜•μ‹ μ£Όν•΄λ₯Ό μƒλž΅ν•  수 μžˆλ‹€.

λ§ˆμ§€λ§‰ 예둜 ν•¨μˆ˜ ν•©μ„±(function composition)을 μ‚΄νŽ΄λ³΄μž. ν•¨μˆ˜ ν•©μ„±μ—μ„œλŠ” ν•œ ν•¨μˆ˜μ˜ 좜λ ₯을 λ‹€λ₯Έ ν•¨μˆ˜μ˜ μž…λ ₯으둜 κ³΅κΈ‰ν•œλ‹€. 이 ν•¨μˆ˜μ˜ κ΅¬ν˜„ μ—­μ‹œ μ „μ μœΌλ‘œ κ·Έ ν˜•μ‹ μ„œλͺ…μœΌλ‘œ κ²°μ •λœλ‹€.

ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œλŠ” 이런 합성이 μ•„μ£Ό μΌμ‚¬μ μœΌλ‘œ 쓰이기 떄문에, 이λ₯Ό μœ„ν•΄ 슀칼라의 ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬λŠ” Function1의 compose λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€. 두 ν•¨μˆ˜ f와 gλ₯Ό ν•¨μ„±ν•˜λ €λ©΄ κ·Έλƒ₯ f compose g라고 ν•˜λ©΄ λœλ‹€. λ˜ν•œ 이 μΈν„°νŽ˜μ΄μŠ€λŠ” andThenμ΄λΌλŠ” λ©”μ„œλ“œλ„ μ œκ³΅ν•œλ‹€. f andThen gλŠ” g compose f와 κ°™λ‹€.

val f = (x: Double) => math.Pi / 2 - x
// Double => Double => <function1>
val cos = f andThen math.sin
// Double => Double => <function1>

compose 같은 κ³ μ°¨ ν•¨μˆ˜λŠ” μžμ‹ μ΄ 수백만 μ€„μ˜ μ½”λ“œλ‘œ 이루어진 κ±°λŒ€ν•œ ν•¨μˆ˜λ₯Ό λ‹€λ£¨λŠ”μ§€ μ•„λ‹ˆλ©΄ κ°„λ‹¨ν•œ ν•œ μ€„μ§œλ¦¬ ν•¨μˆ˜λ₯Ό λ‹€λ£¨λŠ”μ§€ 신경쓰지 μ•ŠλŠ”λ‹€. λ‹€ν˜•μ μΈ κ³ μ°¨ ν•¨μˆ˜λ“€μ€ κ·Έ 적용 λ²”μœ„κ°€ 극도둝 넓은 κ²½μš°κ°€ λ§Žμ€λ°, μ΄λŠ” 그런 ν•¨μˆ˜κ°€ νŠΉμ • λ¬Έμ œμ˜μ—­μ— λŒ€ν•΄ νŠΉλ³„ν•œ μš”κ΅¬λ₯Ό 가지고 μžˆμ§€ μ•ŠμœΌλ©° 였직 λ‹€μˆ˜μ˜ λ¬Έλ§₯μ—μ„œ λ°œμƒν•˜λŠ” κ³΅ν†΅μ˜ νŒ¨ν„΄μ„ μΆ”μƒν™”ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. 이 λ•Œλ¬Έμ— 큰 규λͺ¨μ˜ ν”„λ‘œκ·Έλž˜λ°λ„ μž‘μ€ 규λͺ¨μ˜ ν”„λ‘œκ·Έλž˜λ°κ³Ό μ•„μ£Ό λΉ„μŠ·ν•œ λŠλ‚ŒμœΌλ‘œ 진행할 수 μžˆλ‹€.