콘텐츠로 이동

1. Intro

Scala Functional Programming

참고: https://www.baeldung.com/scala/functional-programming-series

개요

  • FP는 소프트웨어 개발에 있어서 "순수 함수"를 통해 개발하는 것을 말함
  • 몇 가지 주요한 FP 컨셉으로는 다음이 있음
    • 불변성
    • 참조 투명성
    • 패턴 매칭
    • 함수 합성
    • lazy eval
  • 코드에 대해 더 쉽게 추론할 수 있으며, 테스트/문제 해결이 더 쉬워짐

스칼라 FP 입문

참고: https://www.baeldung.com/scala/functional-programming

  • 개요
    • Scala: OOP + FP를 모두 쓸 수 있는 JVM 언어
  • FP란?
    • 함수를 프로그램 설계의 메인 구성요소로 쓰는 것
    • 함수형 프로그래밍에서는 순수 함수 + 불변 값을 쓰도록 노력해야 함 1. 불변성
      • 상수를 통한 프로그래밍. 변수의 값/상태를 변경할 수 없음. 객체도 마찬가지.
      • 변경 가능한 객체보다 더 thread-safe 2. 순수함수
      • 동일한 입력에 동일한 값을 반환
      • side effect 없음 (단순히 값만을 반환한다는 얘기)
  • 스칼라가 Functional 한 것?

    • 모든 함수는 값이다. 함수는 스칼라에서 first-class citizen 1. Function = First Class Citizen

      • 값에 할당 될 수 있음
      • 타 함수의 매개변수로 넘길 수 있음
      • 타 함수의 리턴 값으로 반환할 수 있음 2. Higher-Order Functions (HOF, 고차함수)
      • 함수가 1급 시민이라는 측면에서 다음 둘 중의 한 속성을 만족함

        • 1개 이상의 함수를 파라미터로 받거나,
          def calcAnything(number: Int, calcFunction: Int => Int): Int = calcFunction(number)
          
          def calcSquare(num: Int): Int = num * num
          
          def calcCube(num: Int): Int = num * num * num
          
          val squareCalculated = calcAnything(2, calcSquare)
          assert(squareCalculated == 4)
          
          val cubeCalculated = calcAnything(3, calcCube)
          assert(cubeCalculated == 27)
          
        • 결과값으로 함수를 반환하거나
          def performArithmeticOperation(num1: Int, num2: Int, operation: String): Int = {
            operation match {
              case "addition" => num1 + num2
              case "subtraction" => num1 - num2
              case "multiplication" => num1 * num2
              case _ => -1
            }
          }
          
          val additionResult = performArithmeticOperation(2, 4, "addition")
          assert(additionResult == 6)
          
          val subtractionResult = performArithmeticOperation(10, 6, "subtraction")
          assert(subtractionResult == 4)
          
          val multiplicationResult = performArithmeticOperation(8, 5, "multiplication")
          assert(multiplicationResult == 40) 
          
      • 약간 Java의 Enum을 활용했던 기억과 비슷한 구조로 코드를 짤 수 있음
        1. 익명 함수
      • HOF 활용 시, 명명된 함수 리턴보다 익명함수/함수 리터럴 반환이 더 편할 수도
      • 함수 리터럴, 람다 표현식이라고 지칭
      • 고전적인 HOF에는 map, filter, fold 등이 있음
        1. Closure
      • 자유 변수 (Free Variable) : local variable X, function param variable X
      • 자유 변수 최소 1개라도 쓰는 함수 => 클로져
        val rate = 0.10 
        val time = 2 
        def calcSimpleInterest(principal: Double): Double = { 
          (principal * rate * time) / 100 
        }
        
        val simpleInterest = 20
        assert(calcSimpleInterest(10000) == simpleInterest) 
        
    1. Currying

      • 여러 인수를 받는 함수를, 인수 하나씩 받는 체인으로 변경하는 것
      • 각 함수는 뒤에 받을 인수를 기다리는 함수로 반환됨! (예시의 add8)
        def addition(x: Int, y: Int): Int = x + y
        
        def curriedAddition(x: Int)(y: Int): Int = x + y
        
        val additionResult = addition(8, 4)
        val conciseCurriedAdditionResult = curriedAddition(8)(4)
        val add8 = curriedAddition(8)
        println(add8(8))
        
    2. Partially Applied Functions

      • 함수형 프로그래밍에서, 파라미터가 있는 함수 호출은 "파라미터에 함수를 applying" 하는 것으로 볼 수 있음
        • 함수가 모든 필요한 파라미터를 가지고 호출되면, 함수는 fully applied 라고 볼 수 있는 것 (아하!)
      • 다만, 매개변수의 일부분만 함수에 적용되는 경우 이를 partially applied 라고 합니당
        • 그래서! 이 경우엔 채워야할 나머지 인수가 포함된 함수가 반환됩니다.
          def calculateSellingPrice(discount: Double, productPrice: Double): Double = {
            (1 - discount/100) * productPrice
          }
          
          // _ 요런거...!
          val discountApplied = calculateSellingPrice(25, _)
          val sellingPrice = discountApplied(1000)
          assert(sellingPrice == 750)