데이터 처리의 참조 투명성을 보장하며, 상태와 가변 데이터 생성을 피하는 프로그래밍 패러다임이다.
특징
- 불변성(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 |