πŸ”™λ’€λ‘œκ°€κΈ°

κ°œμš”

ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°(Functional Programming, FP)은 ν”„λ‘œκ·Έλž˜λ° νŒ¨λŸ¬λ‹€μž„ 쀑 ν•˜λ‚˜λ‘œ, μˆœμˆ˜ν•œ(pure) ν•¨μˆ˜μ˜ κ°œλ…κ³Ό λΆˆλ³€μ„±(immutability), 그리고 λͺ…λ Ήν˜• μŠ€νƒ€μΌμ΄ μ•„λ‹Œ μ„ μ–Έν˜• μŠ€νƒ€μΌμ„ κ°•μ‘°ν•œλ‹€. ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ€ 주둜 μˆ˜ν•™μ μΈ 접근을 톡해 문제λ₯Ό ν•΄κ²°ν•˜λ©°, ν•¨μˆ˜μ˜ μž…λ ₯만이 좜λ ₯에 영ν–₯을 λ―ΈμΉ˜λ„λ‘ μ„€κ³„λœλ‹€. μ΄λŠ” 같은 μž…λ ₯에 λŒ€ν•΄ 항상 같은 좜λ ₯을 λ°˜ν™˜ν•œλ‹€λŠ” 뜻이며, μ΄λŸ¬ν•œ ν•¨μˆ˜λ₯Ό 순수 ν•¨μˆ˜λΌκ³  ν•œλ‹€.

ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ˜ νŠΉμ§•

  1. 순수 ν•¨μˆ˜(Pure Functions): 순수 ν•¨μˆ˜λŠ” λ™μΌν•œ μž…λ ₯이 μ£Όμ–΄μ§€λ©΄ 항상 λ™μΌν•œ 좜λ ₯을 λ°˜ν™˜ν•˜κ³ , λΆ€μž‘μš©(side-effects)이 μ—†λŠ” ν•¨μˆ˜λ₯Ό μ˜λ―Έν•œλ‹€. λΆ€μž‘μš©μ€ ν•¨μˆ˜κ°€ μžμ‹ μ˜ μƒνƒœλ₯Ό λ³€κ²½ν•˜κ±°λ‚˜, ν•¨μˆ˜ μ™ΈλΆ€μ˜ μ–΄λ–€ μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” 것을 λ§ν•œλ‹€.
  2. λΆˆλ³€μ„±(Immutability): ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œλŠ” 데이터가 ν•œ 번 μƒμ„±λ˜λ©΄ κ·Έ μƒνƒœκ°€ λ³€κ²½λ˜μ§€ μ•ŠλŠ”λ‹€. 즉, λ³€μˆ˜κ°€ μ•„λ‹Œ κ°’(value)을 μ‚¬μš©ν•œλ‹€. μ΄λŸ¬ν•œ λΆˆλ³€μ„±μ€ μ½”λ“œλ₯Ό 예츑 κ°€λŠ₯ν•˜κ²Œ λ§Œλ“€κ³ , λ©€ν‹°μŠ€λ ˆλ”© ν™˜κ²½μ—μ„œμ˜ 버그λ₯Ό μ€„μ΄λŠ” 데 도움이 λœλ‹€.
  3. κ³ μ°¨ ν•¨μˆ˜(Higher-Order Functions): κ³ μ°¨ ν•¨μˆ˜λŠ” λ‹€λ₯Έ ν•¨μˆ˜λ₯Ό 인자둜 λ°›κ±°λ‚˜, ν•¨μˆ˜λ₯Ό 결과둜 λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ§ν•œλ‹€. μ΄λŠ” ν•¨μˆ˜λ₯Ό κ°’μ²˜λŸΌ μ‚¬μš©ν•˜μ—¬ ν•¨μˆ˜μ˜ μž¬μ‚¬μš©μ„±κ³Ό λͺ¨λ“ˆμ„±μ„ ν–₯μƒμ‹œν‚¨λ‹€.
  4. μž¬κ·€(Recursion): ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œλŠ” 주둜 반볡문 λŒ€μ‹  μž¬κ·€λ₯Ό μ‚¬μš©ν•˜μ—¬ λ°˜λ³΅μ„ κ΅¬ν˜„ν•œλ‹€. μ΄λŠ” ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ˜ λΆˆλ³€μ„± κ°œλ…κ³Ό 연관이 μžˆλ‹€.
  5. μ„ μ–Έν˜• ν”„λ‘œκ·Έλž˜λ°(Declarative Programming): ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ€ 'μ–΄λ–»κ²Œ(How)'λ³΄λ‹€λŠ” '무엇을(What)'ν•˜λŠ”μ§€μ— μ΄ˆμ μ„ λ§žμΆ˜λ‹€. 즉, ν”„λ‘œκ·Έλž¨μ˜ μƒνƒœμ™€ μƒνƒœ 변경을 μ„€λͺ…ν•˜λŠ” λͺ…λ Ήν˜• ν”„λ‘œκ·Έλž˜λ°κ³Ό 달리, ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ€ ν”„λ‘œκ·Έλž¨μ˜ κ²°κ³Όλ₯Ό μ–»κΈ° μœ„ν•΄ 계산이 μ–΄λ–»κ²Œ μˆ˜ν–‰λ˜μ–΄μ•Ό ν•˜λŠ”μ§€λ₯Ό μ„€λͺ…ν•œλ‹€.

Java, JavaScript, Python λ“± λ§Žμ€ ν˜„λŒ€ ν”„λ‘œκ·Έλž˜λ° 언어듀은 ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ° νŒ¨λŸ¬λ‹€μž„μ„ μ§€μ›ν•˜λ©°, 특히 데이터 μ²˜λ¦¬μ™€ 병렬 μ²˜λ¦¬μ— 이점이 μžˆλ‹€. μ΄λŸ¬ν•œ 이유둜 ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ€ λŒ€κ·œλͺ¨ 데이터λ₯Ό μ²˜λ¦¬ν•˜λŠ” λΉ… 데이터 μ• ν”Œλ¦¬μΌ€μ΄μ…˜, λ™μ‹œμ„±μ„ λ‹€λ£¨λŠ” λ©€ν‹°μŠ€λ ˆλ“œ μ‹œμŠ€ν…œ λ“±μ—μ„œ 주둜 ν™œμš©λ˜κ³  μžˆλ‹€.

μžλ°”μ—μ„œμ˜ FP

μžλ°”λŠ” μ›λž˜ 객체 μ§€ν–₯ ν”„λ‘œκ·Έλž˜λ° 언어이닀. κ·ΈλŸ¬λ‚˜ μžλ°” 8λΆ€ν„° ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ˜ κ°œλ…μ„ λ„μž…ν•˜μ—¬ 이 두 κ°€μ§€ νŒ¨λŸ¬λ‹€μž„μ„ λ™μ‹œμ— μ‚¬μš©ν•  수 있게 λ˜μ—ˆλ‹€. μžλ°”μ—μ„œμ˜ ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—λŠ” 주둜 λžŒλ‹€ ν‘œν˜„μ‹(Lambda expressions), λ©”μ†Œλ“œ μ°Έμ‘°(Method references), ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€(Functional interfaces) λ“±μ˜ 문법적 μš”μ†Œλ“€μ΄ μžˆλ‹€.

  1. λžŒλ‹€ ν‘œν˜„μ‹(Lambda Expressions): λžŒλ‹€ ν‘œν˜„μ‹μ€ 이름 μ—†λŠ” ν•¨μˆ˜λ₯Ό λœ»ν•˜λ©°, ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œ 핡심적인 역할을 ν•œλ‹€. λžŒλ‹€ ν‘œν˜„μ‹μ€ κ°„κ²°ν•˜κ²Œ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 있게 ν•΄μ£Όκ³ , ν•¨μˆ˜λ₯Ό λ‹€λ₯Έ ν•¨μˆ˜μ˜ 인자둜 μ „λ‹¬ν•˜κ±°λ‚˜ 결과둜 λ°˜ν™˜ν•  수 있게 ν•œλ‹€.

    // λžŒλ‹€ ν‘œν˜„μ‹ μ˜ˆμ‹œ
    BinaryOperator<Integer> add = (a, b) -> a + b;
    int sum = add.apply(5, 3); // κ²°κ³Ό: 8
    

    μœ„ μ½”λ“œμ— 적용된 λžŒλ‹€ ν‘œν˜„μ‹μ„ 뢄석해보면 λ‹€μŒκ³Ό κ°™λ‹€.

    λ”°λΌμ„œ μ „μ²΄μ μœΌλ‘œ (a, b) -> a + b λžŒλ‹€ ν‘œν˜„μ‹μ€ "a와 bλΌλŠ” 두 μž…λ ₯을 λ°›μ•„μ„œ 이듀을 λ”ν•œ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•œλ‹€"λΌλŠ” λ™μž‘μ„ ν‘œν˜„ν•œλ‹€.

  2. λ©”μ†Œλ“œ μ°Έμ‘°(Method References): λ©”μ†Œλ“œ μ°Έμ‘°λŠ” νŠΉμ • λ©”μ†Œλ“œλ₯Ό 직접 μ°Έμ‘°ν•˜μ—¬ λžŒλ‹€ ν‘œν˜„μ‹κ³Ό 같이 ν•¨μˆ˜λ₯Ό λ‹€λ£¨λŠ” 데 μ‚¬μš©ν•œλ‹€. 이λ₯Ό 톡해 μ½”λ“œλ₯Ό λ”μš± κ°„κ²°ν•˜κ²Œ λ§Œλ“€ 수 μžˆλ‹€.

    // λ©”μ†Œλ“œ μ°Έμ‘° μ˜ˆμ‹œ
    List<String> words = Arrays.asList("Java", "Python", "C++");
    words.stream().forEach(System.out::println);
    
  3. ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€(Functional Interfaces): ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” ν•˜λ‚˜μ˜ 좔상 λ©”μ†Œλ“œλ₯Ό κ°€μ§€λŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ§ν•œλ‹€. λžŒλ‹€ ν‘œν˜„μ‹μ€ 이 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ μΈμŠ€ν„΄μŠ€λ‘œ 간주될 수 μžˆλ‹€. μžλ°” 8μ—μ„œλŠ” java.util.function νŒ¨ν‚€μ§€λ₯Ό 톡해 λ‹€μ–‘ν•œ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ œκ³΅ν•œλ‹€.

    // ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ μ˜ˆμ‹œ
    Predicate<Integer> isEven = n -> n % 2 == 0;
    boolean result = isEven.test(10); // κ²°κ³Ό: true