본문 바로가기
CS/Java

함수형 프로그래밍

by clearinging 2022. 1. 18.
반응형

필자의 생각

  • 비즈니스 로직 복잡도가 높아짐에 따라 사이드 이펙트를 유지 보수성에 따라서 함수형이 뜨고 있습니다
  • 필자는 Java카테고리에 넣기에는 애매한 부분이 있지만 다른 카테고리에 넣기 애매해서 java category에 넣는 걸 결정 했습니다

1. 함수형 프로그래밍

등장배경

  • 비즈니스 로직이 복잡해 지기 때문에 여러 코드와 비즈니스 로직이 스파게티와 같이 엉키는 이슈가 자주 발생
  • 사이드 이펙트와 같은 유지 보수에 어려움을 유발하는 요소들이 많이 발생하게 되었습니다.
  • 사이드 이팩트를 제거 하기 위해서 일관적인 로직을 순수 함수로 정의하여 가독성을 높이고 유지 보수에 편리함을 제공하기 위해 나왔습니다.

2. 함수형 프로그래밍의 특징

순수 함수

  1. 정의
  • 부수효과가 없는 함수 즉, 어떤 함수에 동일한 인자를 주었을 때 항상 같은 값을 리턴하는 함수이며, 외부의 상태를 변경하지 않는 함수(참조 투명성)
  • cf) 부수 함수: 외부의 상태를 변경하는 것 또는 함수로 들어온 인자의 상태를 직접 변경하는 것을 의미
  1. 참조 투명성
  • 동일한 값이 들어가면 동일한 반환값이 보장 되야함(멱등성)
  • 기존 값은 변경 되면 안된다(Immutable Data)
  • 외부 사이드 이펙트가 없는 것을 의미함
  • 이때 부수 효과가 없는/참조 투명성이 보장된 순수한 함수를 1급 객체로 간주하여 파라미터로 전달 및 반환 합니다.
  1. 1급 객체/함수
  • 정의: 1급 시민의 조건을 충족하는 객체를 의미
  • 1급 시민
    • 변수에 담을 수 있어야한다.
    • 함수/method의 인자로 전달할 수 있다
    • 함수/method에 반환값이 될 수 있다
  • Method도 1등 함수가 될 수 있습니다.
    • js/swift/kotlin에서는 지원하고 있습니다.
      • Java에서는 Functional interface를 람다식으로 사용해서 1급 함수 처럼 사용합니다.
    • 예를 들어서 10개의 인자가 필요한 비즈니스 로직이 있습니다.
    • 만약 이 함수를 실행하기 위해서 10인자 모두 준비될 때까지 아무 것도 하지 못합니다.
    • 그리고 read ability가 나빠 집니다.
    • 여러 함수로 나눌 수 있지만 이걸 함수형 으로 인자를 받아서 처리하는 형태로 구현할 수 있습니다.
    • 인자의 일부분을 처리하는 함수를 여러개 만들어서 내부에 호출 하는 방식으로 작성하면 코드가 좀 더 간결해 집니다.
  • 사이드 이팩트 없는 순수 함수를 1급 객체로 간주
    • 사이드 이팩트 발생 케이스
      • 기존의 변수의 값이 변경됨
      • 자료 구조의 내부 값을 수정함
      • 객체의 필드 값을 설정함(setter 호출)
      • 콘솔 또는 파일 I/O발생
      • 예외나 오류가 발생하며 실행이 중단 됨
  • 1급 객체를 파라미터로 넘기거나, 반환 값으로 사용 가능합니다
  • 1급객체의 특징
    • 변수나 데이터 구조 안에 담을 수 있는 객체
    • 파라미터로 전달할 수 있는 객체
    • 반환 값으로 사용할 수 있는 객체
    • 할당에 사용된 이름과 무관하게 고유하게 식별될 수 있는 객체
    • 1급 객체 함수에서 중간에 대입문이 없습니다: 어떤 함수를 통해서 결과를 뱉어 내는 과정에서 중간에 연산에서 변수를 선언할 필요가 없는 것입니다. -> 고차 함수들을 적절히 활용 하기 때문에 중간에 변수를 선언하고 대입할 필요가 없습니다.

불변성

  1. 정의: 변하지 않는 값을 의미합니다.
  2. 예시
  • 대표적으로 프리미티브 타입을 생각할 수 있습니다
  • int type은 다른 변수에 집어 넣게 되면 참조가 이뤄지지 않고 새로운 int 형 데이터가 복사되어서 사용 됩니다.
  • 위와 같이 프리미티브 타입처럼 함수에 input 값을 들어가는 값은 변하면 안되며, input 값과 output 값은 다름 메모리에 할당된 객체야 한다는 것을 의미 합니다

고차 함수

  1. 정의: 함수 인자를 전달 받거나, 함수를 결과로 반환 하는 함수를 의미
  2. 종류
  • reduce
  • map
  • filter
  • functional interface
  • ...etc

클로저

  1. 정의
  • 선언할 당시의 context를 기억했다가, 나중에 호출될 때 저장된 context정보에 따라 호출 되는 함수
  • 함수 outer 함수의 파라미터를 저장하고있고, 내부 함수나 참조 하고 있는 함수 자체를 반환 하는 것을 의미
  • 내부 함수와 외부 함수의 맥락을 고려하는 것을 의미
    • 외부 함수의 파라미터를 지역 변수로 사용할 수있음
  1. 사용 사례
  • callback 함수 내부에서 외부 데이터를 사용하고자 할 경우
  • 부분 적용 함수
  • 커링 함수
  1. callback 함수 내부에서 외부 데이터 사용
let fruits = ['apple', 'banana', 'peach'];
let ul = document.createElement('ul');

fruits.forEach(function (fruit){ // (A)
    var li = document.createElement('li');
    li.innerText = fruit;
    li.addEventListener('click', function(){ // (B)
       alert('your choice is' + fruit);
    });
    ul.appendChild(li);
});
document.body.appendChild(ul);
  • A단계 에서는 외부 변수를 사용하고 있으므로 클로저가 없습니다.
  • li tag의 event 리스너로 등록된 callback 함수 B는 fruit를 참조하는 클로저 입니다.
  • A단계가 종료 되더라도 버튼 클릭 이벤트 함수에서는 fruit 값을 저장하기 때문에 fruits 값이 변경되도 이전에 저장된 값이 호출 됩니다.
  1. 부분 적용 함수
  • n개의 인자를 함수를 2개 이상으로 나눠서 m 개 n - m개의 데이터를 받아서 부분적으로 처리하기 위해서 사용하는 방식입니다.
  1. 커링 함수
  • 여러 개의 인자를 받는 함수를 하나 이상의 인자만 받는 함수로 나눠서 순차 적으로 호출 될 수 있게 하는 것
  • 예시로 여러개의 데이터를 전체 합을 구하는 함수를 구현 한다고 생각합니다
public class FunctionTest {
  public static final Function<String, Function<String, Function<String, String>>> GREETING = message -> name -> result -> message + ", " + name + ": " + result + '!';

  public static void main(String[] args) {
    String fullMessage = GREETING.apply("메시지")
            .apply("이름")
            .apply("결과");

    System.out.println(fullMessage); // 안녕, 세계!
  }
}
// result : 메시지, 이름: 결과!
  • 위예시는 stream과 같이 파이프라인에 좀 더 가깝습니다.
let curry = func => a => b => c => d => e => func(a, b, c, d, e);

curry(a)(b)(c)(d)(e)
  • js 에서 구현한 curring 입니다
  • 화살표 순서에 따라서 함수에 값을 차례로 넘겨주며 마지막에 func이 실행 됩니다.
  • 지연 실행을 하기 위해서 사용함
  1. 람다/함수와 클로저 차이점
  • 람다는 클로저 모두 익명의 특정 기능을 수행 하는 블록입니다.
  • 클로저는 호출 시점이 아닌 클로저 생성 시점의 외부 context 정보를 저장 하고 있습니다. 그러므로 외부 함수를 더이상 접근 하지 못하더라도 기억하고 있는 context 정보가 사라지지 않는다.
반응형

'CS > Java' 카테고리의 다른 글

SOLID 원칙  (0) 2022.02.06
예외(Exception)  (0) 2022.01.20
LocalDatetime, LocalDate, LocalTime  (0) 2022.01.12
Thread  (0) 2022.01.01