Functional Programming

함수형 프로그래밍의 특징

Binceline 2022. 5. 19. 07:50

데이터 처리의 참조 투명성을 보장하며, 상태와 가변 데이터 생성을 피하는 프로그래밍 패러다임이다.

특징

  • 불변성(Immutable)
  • 참조 투명성(Referential Transparency)
  • 일급 함수(First Class Function)
  • 게으른 평가(Lazy Evaluation)

동시성, 병렬성

동시성 프로그래밍에 적합하다.

  • 동시성(Concurrency): 동시에 실행하는 것처럼 보이도록 하는 것
  • 병렬성(Parallel): 실제로 물리적으로 동시에 돌아가는 것

코드 복잡도가 낮아 간결한 코드 작성 가능

순수 함수(Pure Function)

특징

  • y = f(x). 동일한 input은 동일한 output을 가짐.
    • 상수가 아닌 전역 변수 등의 외부 변수를 이용하는 코드가 존재하면 순수 함수가 아니다.
  • 부수효과가 없음: 함수가 실행되는 과정에서 외부의 상태에 의존하거나, 외부의 상태를 수정하지 않는다.
    • 부수효과: 외부 상태에 영향을 미치는 것
      • 파일, 네트워크 I/O
      • 예외 발생

순수 함수를 이용하게 되면 동시성 관련 코드를 작성할 때, 공유 자원이 변경될 걱정 없이 안전하게 코드를 작성할 수 있다.

순수 함수가 아닌 함수의 특징

  • Output에 대한 예측이 어렵다.
  • 외부 변수 수정 등의 부수효과가 발생하기 때문에 어떤 문제가 발생할지도 예측이 어렵다.
  • 테스트하기도 어렵다.

당연하게도 순수 함수가 아닌 함수를 이용하는 함수도 마찬가지로 순수 함수가 아니게 된다.

참조 투명성: 표현식을 값으로 대체할 수 있는가?

  • 1 + 1은 2로 대체될 수 있다.
  • 순수함수 y = f(x)가 주어졌을 때, f(x)가 y를 반환하므로 f(x)는 y로 대체 가능하다.

일급

일급 객체(First Class Object)

다음 3가지를 만족하는 객체를 뜻한다.

  • 객체를 함수 파라미터로 넘길 수 있다.
  • 객체를 함수 반환 값으로 넘길 수 있다.
  • 객체를 변수, 자료구조에 담을 수 있다. ex) val anyList: List<Any> = listOf(Any())

일급 함수(First Class Function)

위의 3가지 조건에 객체 -> 함수로 생각하면 된다.

  • 함수를 함수 파라미터로 넘길 수 있다.
    • ex) fun doSomething(func: (Int) -> String) { }
  • 함수를 함수 반환값으로 넘길 수 있다.
    • ex) fun doSomething() : (Int) -> String { return { value -> value.toString() } }
  • 함수를 변수, 자료구조에 담을 수 있다.
    • ex) var funcList : List<(Int) -> String> = listOf({ value -> value.toString() })

일급 함수를 이용하면 명령형 프로그래밍, 객체지향 프로그래밍에서 할 수 없는 수준의 추상화가 가능하다.

 

일급 함수를 이용한 추상화 / 재사용성 높이기

명령형 / 객체지향 프로그래밍에서 할 수 없는 추상화가 가능하다.

객체가 아닌, 함수를 DI할 수 있기 때문이다.

자세한 예제는 책에.

 

Lazy Evaluation으로 무한 자료구조 만들기

val lazyValue: String by lazy {
	println("taking long time")
    "hello"
}

fun main(args: Array<String>) {
	println(layValue)
	println(layValue)
}

실행하면 다음과 같은 결과를 볼 수 있다.

 

taking long time

hello

hello

 

by lazy

  • 값을 by lazy로 선언하고 매개변수로 람다 식을 넘기면 해당 인스턴스가 호출되는 시점에 람다 식이 한 번 실행된다.
    • 여러 번 호출되어도 최초 한 번만 평가를 실행한다.
    • 내부적으로 결과값을 저장해 두고, 필요할 때 가져온다.
    • 값이 최초 한 번만 평가되기 때문에, 수정이 불가능하고, 그렇기 때문에 lazy는 var로 선언할 수 없다.
  • lazy를 이용하지 않는다면 "taking long time" 이 2번 평가된다.

무한대 값을 자료구조에 담기

명령형 언어에서는 일반적으로 무한대 값을 자료구조에 담을 수 없다. 있다면 무한 루프의 위험이 존재한다.

함수형 언어에서는 Lazy Evaluation이라는 특성을 통해 이를 가능하게 한다.

val infValue = generateSequence(0) { it + 5 } // start from 0, then run this function. ex) f(f(f(0)))
infValue.take(5).forEach { 
	print("$it ") 
}

// "0 5 10 15 20" 출력

 

infValue는 generateSequence() 함수가 호출된 시점에 평가되지 않기 때문에(Lazy) 이러한 표현이 가능한 것이다.

반응형

'Functional Programming' 카테고리의 다른 글

[Kotlin] Interface와 Class  (0) 2022.05.27
Kotlin 함수 선언 방법  (0) 2022.05.26