π Chapter 1: ν¨μν νλ‘κ·Έλλ°μ΄λ 무μμΈκ°?
ν¨μν νλ‘κ·Έλλ°(functional programming, FP)μ κ°λ¨νμ§λ§ μ¬μ€ν λ»μ λ΄μ μ μ μ κΈ°μ΄νλ€. κ·Έ μ μ λ, νλ‘κ·Έλ¨μ μ€μ§ μμ ν¨μ(pure function)λ€λ‘λ§, λ€μ λ§ν΄μ λΆμ ν¨κ³Ό(side effect)κ° μλ ν¨μλ€λ‘λ§ κ΅¬μΆνλ€λ κ²μ΄λ€. λΆμ ν¨κ³Όκ° 무μμΌκΉ? κ·Έλ₯ κ²°κ³Όλ₯Ό λλ €μ£Όλ κ² μ΄μΈμ μ΄λ€ μΌμ μννλ ν¨μλ₯Ό κ°λ¦¬μΌ λΆμ ν¨κ³Όκ° μλ ν¨μλΌκ³ μΉνλ€. "κ·Έλ₯ κ²°κ³Όλ₯Ό λλ €μ£Όλ κ² μ΄μΈμ μ΄λ€ μΌ"μ μλ₯Ό λͺ κ°μ§ λ€μλ©΄ λ€μκ³Ό κ°λ€.
- λ³μλ₯Ό μμ νλ€.
- μλ£κ΅¬μ‘°λ₯Ό μ μ리μμ μμ νλ€.
- κ°μ²΄μ νλλ₯Ό μ€μ νλ€.
- μμΈ(exception)λ₯Ό λμ§κ±°λ μ€λ₯λ₯Ό λ΄λ©΄μ μ€νμ μ€λ¨νλ€.
- μ½μμ μΆλ ₯νκ±°λ μ¬μ©μμ μ λ ₯μ μ½μ΄λ€μΈλ€.
- νμΌμ κΈ°λ‘νκ±°λ νμΌμμ μ½μ΄λ€μΈλ€.
- νλ©΄μ κ·Έλ¦°λ€.
ν¨μν νλ‘κ·Έλλ°μ μ°λ¦¬κ° νλ‘κ·Έλ¨μ μμ±νλ λ°©μμ λν μ μ½μ΄μ§ νν κ°λ₯ν νλ‘ κ·Έλ¨μ μ’ λ₯μ λν μ μ½μ΄ μλλΌλ κ²μ΄λ€. μμ ν¨μλ€λ‘ νλ‘κ·Έλ¨μ μμ±νλ©΄ λͺ¨λμ±(modularity)μ΄ μ¦κ°νλ€. λͺ¨λμ± λλΆμ μμ ν¨μλ κ²μ¬(test), μ¬μ¬μ©, λ³λ ¬ν, μΌλ°ν, λΆμμ΄ μ½λ€. λ λμκ°μ, μμ ν¨μλ λ²κ·Έκ° μκΈΈ μ¬μ§κ° ν¨μ¬ μ λ€.
π FPμ μ΄μ : κ°λ¨ν μμ νλβ
π λΆμ ν¨κ³Όκ° μλ νλ‘κ·Έλ¨β
컀νΌμμμ 컀νΌλ₯Ό ꡬ맀νλ κ³Όμ μ μ²λ¦¬νλ νλ‘κ·Έλ¨μ ꡬννλ€κ³ κ°μ νμ. μ°μ ꡬνμμ λΆμ ν¨κ³Όλ₯Ό μ¬μ©νλ(μ΄λ₯Ό λΆμν[impure] νλ‘κ·Έλ¨μ΄λΌκ³ λΆλ₯΄κΈ°λ νλ€) μ€μΉΌλΌ νλ‘κ·Έλ¨λΆν° 보μ.
class Cafe {
def buyCoffee(cc: CreditCard): Coffee = {
val cup = new Coffee()
// λΆμ ν¨κ³Ό, μ μ©μΉ΄λλ₯Ό μ€μ λ‘ μ²κ΅¬νλ€.
cc.charge(cup.price)
cup
}
}
μ μ©μΉ΄λ μ²κ΅¬μλ μΈλΆ μΈκ³μμ μΌμ ν μνΈμμ©μ΄ κ΄μ¬νλ€. κ·Έλ¬λ μ΄ ν¨μ μ체λ λ¨μ§ νλμ Coffee
κ°μ²΄λ₯Ό λλ €μ€ λΏμ΄κ³ , κ·Έ μΈμ λμμ λͺ¨λ λΆμμ μΌλ‘(on the side) μΌμ΄λλ€.
λΆμ ν¨κ³Όκ° μκΈ° λλ¬Έμ μ΄ μ½λλ κ²μ¬νκΈ°κ° μ΄λ ΅λ€. μ½λλ₯Ό κ²μ¬νκΈ° μν΄ μ€μ λ‘ μ μ©μΉ΄λ νμ¬μ μ°κ²°ν΄μ μΉ΄λ μ΄μ© λκΈμ μ²κ΅¬νκ³ μΆμ§λ μκΈ° λλ¬Έμ΄λ€. CreditCard
λμ μ§κΈμ μν Payments
κ°μ²΄λ₯Ό buyCoffee
μ μ λ¬νλ€λ©΄ μ½λμ λͺ¨λμ±κ³Ό κ²μ¬μ±μ μ’ λ λμΌ μ μλ€.
class Cafe {
def buyCoffee(cc: CreditCard, p: Payments): Coffee = {
val cup = new Coffee()
p.charge(cc, cup.price)
cup
}
}
p.charge(cc, cup.price)
λ₯Ό νΈμΆν λ μ¬μ ν λΆμ ν¨κ³Όλ₯Ό λ°μνμ§λ§, μ μ΄λ κ²μ¬μ±μ μ‘°κΈ λμμ‘λ€. μ΄ κ΅¬νμλ buyCoffee
λ₯Ό μ¬μ¬μ©νκΈ° μ΄λ ΅λ€λ λ λ€λ₯Έ λ¬Έμ κ° μλ€.
π ν¨μμ ν΄λ²: λΆμ ν¨κ³Όμ μ κ±°β
μ΄μ λν ν¨μμ ν΄λ²μ λΆμ ν¨κ³Όλ€μ μ κ±°νκ³ buyCoffee
κ° Coffee
λΏλ§ μλλΌ μ²κ΅¬κ±΄μ νλμ κ°μΌλ‘ λλ €μ£Όκ² νλ κ²μ΄λ€. μ²κ΅¬ κΈμ‘μ μ μ©μΉ΄λ νμ¬μ 보λ΄κ³ κ²°κ³Όλ₯Ό κΈ°λ‘νλ λ±μ μ²λ¦¬ λ¬Έμ λ buyCoffee
λ°κΉ₯μ λ€λ₯Έ μ΄λκ°μμ ν΄κ²°νλλ‘ νλ€.
class Cafe {
def buyCoffee(cc: CreditCard): (Coffee, Charge) = {
val cup = new Coffee()
(cup, Charge(cc, cup.price))
}
}
μ΄μ μ²κ΅¬κ±΄μ μμ± λ¬Έμ κ° μ²κ΅¬κ±΄μ μ²λ¦¬ λλ μ°λ λ¬Έμ μ λΆλ¦¬λμλ€. buyCoffee
ν¨μλ μ΄μ Coffee
λΏλ§ μλλΌ Charge
λ λλ €μ€λ€. μ΄λ° λ³κ²½ λλΆμ μ¬λ¬ μμ 컀νΌλ₯Ό ν λ²μ κ±°λλ‘ κ΅¬λ§€νκΈ° μν΄ μ΄ ν¨μλ₯Ό μ¬μ¬μ©νκΈ°κ° μ¬μμ‘μμ μ μ νμ λ³΄κ² λ κ²μ΄λ€. λ€μμ Charge
μ΄λ€.
case class Charge(cc: CreditCard, amount: Double) {
def combine(other: Charge): Charge =
if (cc == other.cc)
Charge(cc, amount + other.amount)
else
throw new Exception("Can't combine charges to different cards")
}
κ·ΈλΌ μ»€νΌ nμμ ꡬ맀μ ꡬνν buyCoffees
ν¨μλ₯Ό 보μ. μ΄μ κ³Όλ λ¬λ¦¬, μ΄ ν¨μλ buyCoffee
λ₯Ό μ΄μ©ν΄μ ꡬνλμ΄ μλ€.
class Cafe {
def buyCoffee(cc: CreditCard): (Coffee, Charge) = {
val cup = new Coffee()
(cup, Charge(cc, cup.price))
}
def buyCoffees(cc: CreditCard, n: Int): (List[Coffee], Charge) = {
val purchases: List[(Coffee, Charge)] = List.fill(n)(buyCoffee(cc))
val (coffees, charges) = purchases.unzip(coffees, charges.reduce((c1, c2) => c1.combine(c2)))
}
}
μ λ°μ μΌλ‘, μ΄ ν΄λ²μ μ΄μ λ³΄λ€ λλ ·μ΄ κ°μ λμλ€. μ΄μ λ buyCoffee
λ₯Ό μ§μ μ¬μ¬μ©ν΄μ buyCoffees
ν¨μλ₯Ό μ μν μ μμΌλ©°, λ ν¨μ λͺ¨λ Payments
μΈν°νμ΄μ€μ 볡μ‘ν λͺ¨μ ꡬνμ μ μνμ§ μκ³ λ μμ½κ² κ²μ¬ν μ μλ€. μ€μ λ‘ Cafe
λ μ΄μ Charge
μ λκΈμ΄ μ΄λ»κ² μ²λ¦¬λλμ§ μ ν μμ§ λͺ»νλ€.
Charge
λ₯Ό μΌκΈ(first-class) κ°μΌλ‘ λ§λ€λ©΄, μ²κ΅¬κ±΄λ€μ λ€λ£¨λ μ
무 λ
Όλ¦¬λ₯Ό μ’ λ μ½κ² 쑰립ν μ μλ€λ μμμΉ λͺ»νλ λ λ€λ₯Έ μ΄λμ΄ μκΈ΄λ€.
FPλ μμ£Ό κ°λ¨ν 루νμμλΆν° κ³ μμ€ νλ‘κ·Έλ¨ κΈ°λ°κ΅¬μ μ μ΄λ₯΄κΈ°κΉμ§ λͺ¨λ μμ€μμ νλ‘κ·Έλ¨μ μ‘°μ§ν λ°©μμ μμ£Ό κΈμ§μ μΌλ‘ λ³νμν¨λ€. FPμμ νμλλ μ€νμΌμ λ€λ₯Έ νλ‘κ·Έλλ°μ κ²κ³Ό μλΉν λ€λ₯΄μ§λ§, κ·Έκ²μ μλ¦λ΅κ³ μμ§λ ₯ μλ νλ‘κ·Έλλ° μ κ·Όλ°©μμ΄λ€.
π (μμ)ν¨μλ ꡬ체μ μΌλ‘ 무μμΈκ°?β
μ
λ ₯ νμμ΄ Aμ΄κ³ μΆλ ₯ νμμ΄ BμΈ ν¨μ fλ νμμ΄ AμΈ λͺ¨λ κ° aλ₯Ό κ°κ° νμμ΄ BμΈ νλμ κ° bμ μ°κ΄μν€λ, bκ° μ€μ§ aμ κ°μ μν΄μλ§ κ²°μ λλλ 쑰건μ λ§μ‘±νλ κ³μ°μ΄λ€. λ΄λΆ λλ μΈλΆ 곡μ μ μν λ³κ²½μ f(a)μ κ²°κ³Όλ₯Ό κ³μ°νλ λ° μ΄λ ν μν₯λ μ£Όμ§ μλλ€. μλ₯Ό λ€μ΄ Int => String
νμμ intToString
ν¨μλ λͺ¨λ μ μλ₯Ό κ·Έμ λμλλ λ¬Έμμ΄μ λμμν¨λ€. κ·Έλ¦¬κ³ μ΄κ²μ΄ λ§μΌ μ€μ ν¨μ(function)μ΄λ©΄, κ·Έ μΈμ μΌμ μ ν νμ§ μλλ€.
λ€λ₯Έ λ§λ‘ νλ©΄, ν¨μλ μ£Όμ΄μ§ μ λ ₯μΌλ‘ λκ°λ₯Ό κ³μ°νλ κ² μΈμλ νλ‘κ·Έλ¨μ μ€νμ κ·Έ μ΄λ€ κ΄μ°° κ°λ₯ν μν₯λ λ―ΈμΉμ§ μλλ€. μ΄λ₯Ό λκ³ ν¨μμ λΆμ ν¨κ³Όκ° μλ€κ³ λ§νλ€. κ·Έλ° ν¨μλ₯Ό μ’ λ λͺ μμ μΌλ‘ μμ(pure) ν¨μλΌκ³ λΆλ₯΄κΈ°λ νμ§λ§, μ΄λ λ€μ κ΅°λλκΈ°μ΄λ€. νΉλ³ν μΈκΈμ΄ μλ ν, μ΄ μ± μμ ν¨μλ λΆμ ν¨κ³Όκ° μλ ν¨μλ₯Ό λ»νλ€.
μμ ν¨μμ μ΄λ¬ν κ°λ
μ μ°Έμ‘° ν¬λͺ
μ±(referential transparency, RT)μ΄λΌλ κ°λ
μ μ΄μ©ν΄μ 곡μνν μ μλ€. μ°Έμ‘° ν¬λͺ
μ±μ ν¨μκ° μλλΌ ννμ(expression)μ ν μμ±μ΄λ€. μλ₯Ό λ€μ΄ 2 + 3
μ νλμ ννμμ΄λ€. μ΄ ννμμ κ° 2μ 3μ μμ ν¨μ +
λ₯Ό μ μ©νλ€. μ΄ ννμμλ λΆμ ν¨κ³Όκ° μλ€. μ΄ ννμμ νκ°λ νμ 5λΌλ κ°μ κ²°κ³Όλ₯Ό λΈλ€. μ€μ λ‘, νλ‘κ·Έλ¨μ μλ λͺ¨λ 2 + 3
μ κ° 5λ‘ λ°κΎΈμ΄λ νλ‘κ·Έλ¨μ μλ―Έλ μ ν λ°λμ§ μλλ€.
μ΄κ²μ΄ λ°λ‘ ννμμ μ°Έμ‘° ν¬λͺ μ±μ μ λΆμ΄λ€. μ¦, μμμ νλ‘κ·Έλ¨μμ λ§μΌ μ΄λ€ ννμμ κ·Έ νκ° κ²°κ³Όλ‘ λ°κΎΈμ΄λ νλ‘κ·Έλ¨μ μλ―Έκ° λ³νμ§ μλλ€λ©΄ κ·Έ ννμμ μ°Έμ‘°μ ν¬λͺ ν κ²μ΄λ€. κ·Έλ¦¬κ³ λ§μΌ μ΄λ€ ν¨μλ₯Ό μ°Έμ‘°μ ν¬λͺ ν μΈμλ€λ‘ νΈμΆνλ©΄, κ·Έ ν¨μλ μ°Έμ‘°μ ν¬λͺ νλ€.
π μ°Έμ‘° ν¬λͺ μ±, μμμ±, κ·Έλ¦¬κ³ μΉν λͺ¨νβ
μ°Έμ‘° ν¬λͺ
μ±μ μ μκ° μλμ buyCoffee
μμ μ μ΄λ»κ² μ μ©λλμ§ μ΄ν΄λ³΄μ.
def buyCoffee(cc: CreditCard): Coffee = {
val cup = new Coffee()
cc.charge(cup.price)
cup
}
cc.charge(cup.price)
μ λ°ν νμμ΄ λ¬΄μμ΄λ , buyCoffee
λ κ·Έ λ°νκ°μ νκΈ°νλ€. λ°λΌμ buyCoffee(aliceCreditCard)
μ νκ° κ²°κ³Όλ κ·Έλ₯ cup
μ΄λ©°, μ΄λ new Coffee()
μ λλ±νλ€. μμ μ°Έμ‘° ν¬λͺ
μ± μ μνμμ buyCoffee
κ° μμνλ €λ©΄ μμμ(any) pμ λν΄ p(buyCoffee(aliceCreditCard))
κ° p(new Coffee())
μ λμΌνκ² μλν΄μΌ νλ€. μ΄ μ‘°κ±΄μ΄ μ°Έμ΄ μλμ λͺ
λ°±νλ€. new Coffee()
λΌλ νλ‘κ·Έλ¨μ μ무 μΌλ νμ§ μμ§λ§ buyCoffee(aliceCreditCard)
λ μ μ©μΉ΄λ νμ¬μ μ°κ²°ν΄μ λκΈμ μ²κ΅¬νκΈ° λλ¬Έμ΄λ€.
μ°Έμ£Ό ν¬λͺ μ±μ ν¨μκ° μννλ λͺ¨λ κ²μ΄ ν¨μκ° λλ €μ£Όλ κ°μΌλ‘ λνλλ€λ λΆλ³ 쑰건μ κ°μ νλ€. μ΄λ¬ν μ μ½μ μ§ν€λ©΄ μΉν λͺ¨νμ΄λΌκ³ λΆλ₯΄λ, νλ‘κ·Έλ¨ νκ°μ λν κ°λ¨νκ³ λ μμ°μ€λ¬μ΄ μΆλ‘ λͺ¨νμ΄ κ°λ₯ν΄μ§λ€. μ°Έμ‘°μ ν¬λͺ ν ννμλ€μ κ³μ° κ³Όμ μ λ§μΉ λμ λ°©μ μμ ν λμ μμ£Ό λΉμ·νλ€. μ¦, ννμμ λͺ¨ λ λΆλΆμ μ κ°νκ³ , λͺ¨λ λ³μλ₯Ό ν΄λΉ κ°μΌλ‘ μΉννκ³ , κ·Έλ° λ€μ κ·Έκ²λ€μ κ°μ₯ κ°λ¨ν ννλ‘ νμ(μΆμ½)νλ©΄ λλ€. κ° λ¨κ³λ§λ€ νλμ νμ κ·Έμ λλ±ν κ²μΌλ‘ λ체νλ€. μ¦ κ³μ°μ λ±μΉ λ λ±μΉ(equals for equals) μΉνμ ν΅ν΄μ μ§νλλ€. λ€λ₯Έ λ§λ‘ νλ©΄, μ°Έμ‘° ν¬λͺ μ±μ νλ‘κ·Έλ¨μ λν λ±μμ μΆλ‘ (equational reasoning)μ κ°λ₯νκ² νλ€.
μΉν λͺ¨ν μΆλ‘ μ νκ°μ λΆμ ν¨κ³Όκ° μ μ μΌλ‘ κ΅μμ μ΄λ€. λ°λΌμ μ½λ λΈλ‘μ μ΄ν΄νκΈ° μν΄μ λ¨Έλ¦Ώμμμ μΌλ ¨μ μν κ°±μ λ€μ λ°λΌκ° νμκ° μλ€. κ΅μ μΆλ‘ (local reasoning)λ§μΌλ‘λ μ½λλ₯Ό μ΄ν΄ν μ μλ€. ν¨μμ μ€ν μ΄μ κ³Ό μ΄νμ λ°μν μ μλ λͺ¨λ μν λ³νλ€μ λ¨Έλ¦ΏμμΌλ‘ μ§μ΄ λκ°μ§ μκ³ λ ν¨μκ° νλ μΌμ μ΄ν΄ν μ μλ κ²μ΄λ€. κ·Έλ₯ ν¨μμ μ μλ₯Ό λ³΄κ³ ν¨μ λ³Έλ¬Έμμ μΈμλ€μ μΉννκΈ°λ§ νλ©΄ λλ€. λ°λ‘ μΉν λͺ¨νμΌλΌλ ꡬ체μ μΈ μ©μ΄λ₯Ό μ¬μ©νμ§ μμλλΌλ, μμ μ μ½λλ₯Ό μΆλ‘ ν λ λ μλ μλ§ μ΄λ° λͺ¨νμ μ¬μ©ν΄ μμ κ²μ΄λ€.
μμμ±μ κ°λ μ μ΄λ° μμΌλ‘ 곡μνν΄ λ³΄λ©΄, ν¨μμ νλ‘κ·Έλ¨μ λͺ¨λμ±μ΄ λ€λ₯Έ κ²½μ°μ λΉν΄ λ μ’μ κ²½μ°κ° λ§μ μ΄μ λ₯Ό μ§μν μ μλ€. λͺ¨λμ μΈ νλ‘κ·Έλ¨μ μ 체μλ λ 립μ μΌλ‘ μ΄ν΄νκ³ μ¬μ¬μ©ν μ μλ ꡬμ±μμλ€λ‘ ꡬμ±λλ€. κ·Έλ° νλ‘κ·Έλ¨μμ νλ‘κ·Έλ¨ μ 체μ μλ―Έλ μ€μ§ ꡬμ±μμλ€μ μλ―Έμ ꡬμ±μμλ€μ ν©μ±μ κ΄ν κ·μΉλ€μλ§ μμ‘΄νλ€. μ¦, ꡬμ±μμλ€μ ν©μ± κ°λ₯(composable)νλ€. μμ ν¨μλ λͺ¨λμ μ΄κ³ ν©μ± κ°λ₯νλ°, μ΄λ μμ ν¨μμμ κ³μ° μ체μ λ Όλ¦¬κ° "κ²°κ³Όλ‘ λ¬΄μμ ν κ²μΈκ°"λ "μ λ ₯μ μ΄λ»κ² μ»μ κ²μΈκ°"μλ λΆλ¦¬λμ΄ μκΈ° λλ¬Έμ΄λ€. μ¦, μμ ν¨μλ νλμ λΈλλ°μ€μ΄λ€. μ λ ₯μ΄ μ£Όμ΄μ§λ λ°©μμ λ¨ νλμ΄λ€. μ λ ₯μ νμ ν¨μμ λν μΈμλ€λ‘λ§ μ£Όμ΄μ§λ€. κ·Έλ¦¬κ³ ν¨μλ κ²°κ³Όλ₯Ό κ³μ°ν΄μ λλ €μ€ λΏ, κ·Έκ²μ΄ μ΄λ»κ² μ°μ΄λμ§λ μ κ²½ μ°μ§ μλλ€. μ΄λ¬ν κ΄μ¬μ¬μ λΆλ¦¬ λλΆμ κ³μ° λ Όλ¦¬μ μ¬μ¬μ©μ±μ΄ λμμ§λ€. κ²°κ³ μ κ΄λ ¨λ λΆμ ν¨κ³Όλ μ λ ₯μ μ»λ λ° κ΄λ ¨ λΆμ ν¨κ³Όκ° λͺ¨λ λ¬Έλ§₯μμ μ μ νμ§ κ±±μ νμ§ μκ³ ν¨μλ₯Ό μ¬μ¬μ©ν μ μλ€.