[FP] 함수형 복습

yongkini ·2024년 8월 22일
0

Functional Programming

목록 보기
5/10
  • 일단 바로는 이해 안되겠지만, 함수형 프로그래밍의 진짜 목표는 애플리케이션의 부수효과를 방지하고, 상태 변이를 감소하기 위해 데이터의 제어 흐름과 연산을 추상하는 것이다.
    • 부수효과

    • 상태 변이 감소

    • 데이터의 제어 흐름과 연산 추상

      솔직히 아직 뭔소리인지 잘모르겠지만, 일단 이정도 하고 넘어간다.

  • 함수형 프로그래밍의 기본 원리는 이해하기 쉬운 더 작은 조각들로 프로그램을 나눈 다음에 그 작은 조각들을 조합해서 더 헤아리기 쉬운 프로그램으로 만든다. 이는 모든 함수형 프로그램의 기본 원리이다.
  • 생각해보면, 절차형 프로그래밍으로 코딩을 할 때, 비즈니스 로직이 좀만 커지면, 많은 if 분기처리와 forEach 문 그리고 그 사이에서도 분기 처리 등이 늘어난다. 이렇게 생각했을 때, 앞서 말한 함수형의 기본 원리인 더 작은 프로그램 조각으로 나눈 다음에 그 조각들을 run, pipe, compose 등의 함수형 툴을 이용해 조합해서 쓰는 버릇을 들이면, 매번 앞서 말한 절차형의 분기처리를 복붙해서 다시 쓴다던지, 혹은 복붙하면서 아주 살짝 다른 로직을 처리하기 위해 flag를 쓴다던지 할 필요가 없다. 그냥 새로운 기획 혹은 새로운 분기처리를 위한 함수 조각을 하나 더 만들어서 새로운 조합, 예를 들어, 앞선 조합이 조합 A 였으면 이젠 조합 B로 만들어서 쓰면 된다. 이런 부분에 있어서 재사용성, 확장성이 좋다고 하는 것 같다. 함수에 함수 파라미터를 넣을수도 있다는 것을 생각해보면, 함수형 프로그래밍은 작은 함수 조각들로 확장성 좋은 함수 블록 놀이(?)를 하는거라고 생각해볼 수 있다. 재밌겠다..!
  • 함수형 프로그래밍의 기본 개념들
    • 선언적 프로그래밍
      • for 문을 작성하는 것 vs forEach or map 등의 선언적 툴을 쓰는 것을 생각하면 선언적 프로그래밍이 뭔지 쉽게 알 수 있다.
      • 혹은 절차형 프로그래밍의 반대말로, 각종 분기처리, for문 작성 등을 떠올려보면 이것의 반대인 선언적 프로그래밍도 쉽게 이해된다.
      • 예시
        const getMainQuestion = (ev: Event) =>
          checkIsEmptyString((ev.target as HTMLInputElement).value)
            .chain(validateMainQuestion)
            .orElse(setMainQuestionErrorMessage)
            .chain(resetMainQuestionErrorMessage);
        위에 함수는 내가 만든 함수인데, 쉽게 설명해보면, question이라는 값을 input tag로 받아서(input event handler 함수이다) 인풋 값에 대해서 빈값인지 확인하고(checkIsEmptyString) 빈값이면 Either.left를 반환한다(⇒ 더이상 진행하는게 의미가 없다는 의미로 이해하자).
        export const checkIsEmptyString = (value: string) => (value.length === 0 ? Either.left('') : Either.of(value));
        그러면 그 다음 chain을 건너띄고, orElse 문으로 간다. 거기서는 setMainQuestionErrorMessage 이를 통해 input UI 밑에 보여줄 에러 메세지를 세팅하는데, 방금 경우에는 left 안에 빈 string(’’)을 넣어줬으므로 에러 메시지가 출력되지 않는다. 그럼 방금 checkIsEmptyString에서 빈배열이 아니었어서 Either.of(=Either.right) 를 리턴하고, 다음 체인으로 넘어갔다고 해보자. 그럼 다음 체인에서는 validateMainQuestion 을 통해 해당 인풋값을 정해진 로직으로 유효성 검사를 실행한다. 이 때, 유효성 검사를 통과하면 Right를 리턴해서 orElse를 통과하고, 실패하면 에러 메시지와 함께(해당 유효성 검사에 맞는) orElse에 걸린다. 그리고 마지막에 있는 resetMainQuestionErrorMessage 이건 만약 input 값에 에러가 없어서, 즉, 유효성 검사를 통과한 경우 orElse를 통과해서 마지막에 에러 메시지를 ‘’로 리셋해주기 위한 로직이다. 이걸 실행하는 이유는 input을 입력하다가 에러가 생겨서 에러메시지를 화면에 출력했는데, 다음 input이 추가되고서는 유효성 검사를 통과했다면 이전 에러 메시지는 지워줘야하기 때문에 reset 로직을 추가해둔 것이다. 설명이 길었는데 포인트는 여태까지 설명한 로직들은 저 함수의 이름들만으로 선언적으로 이해할 수 있고, 굳이 저 안에 내부 로직을 설명할 필요도 알아볼 필요도, 알아볼수도 없다는게 선언적 프로그래밍의 포인트라고 할 수 있다. 만약 저게 절차형 프로그래밍이었으면, if(value === ‘’) return 이런식의 로직들이 있을 것이고, 그 절차를 따라가면서 코드를 이해해야 했을 것이다. 하지만, 지금은 아 checkIsEmptyString → 아 빈 스트링인지 확인하고, chaining으로 유효성 검사를 하는구나 등등 선언적으로 로직을 파악할 수 있고, 그 내부를 알 필요가 없다(물론 내가 변수명을 잘지었는지는 논외다).
    • 순수함수
    • 참조 투명성
    • 불변성
  • 함수형 프로그램은 무상태성불변성을 지향한다.
    • 무상태성 : 아직은 잘 모르겠지만, 전역 변수라던지, 외부 state(?) 등 함수 내에 있는 변수 등에 접근하는게 아니라 함수 바깥에 있는 변수 혹은 상태들에 접근해서 어떤 영향을 미치는 것이 아닌 것. 즉, 이런 상태에 대한 컨트롤? 건드는 부분이 없는게 무상태성이라고 생각해보자. 순수함수와 연관되는 이야기일 것 같다.
      • 순수함수 : I just understand this like this way. if we want to say this is pure function, then it must not reference some variable from outside of function. and also not change value of the variable referenced from outside. As a result, think pure function like this. if some function want to be called pure function, It must keep returning same result when it got same parameter. For example, if pure function got A with prameter then it should always return B. This is because pure function doesn’t reference some other states or variable from outside and doesn’t change these states or variables. By doing so, pure functions are always returning same result if they got same parameters. I think this is similar with mathematics like if we plus 1 and the other 1, result is always 2. Like this, pure function always retuns same result when same input is inserted.
        • 예시

          ```jsx
          export const isValid = <T>(value: T) => value !== undefined && value !== null;
          ```
          
          그리고 위에 순수함수를 이용해서 만든 또다른 순수함수는 아래와 같다.
          
          ```jsx
          export const returnArrayOrEmptyArray = <T>(value: NullAndUndefined | T[]): T[] => {
            return isValid(value) ? value : [];
          };
          ```

          그러나 현실적으로, 순수함수로만 프로그램을 만들순 없다. 생각해보면, 유저 인터페이스에 따른 state 변동 및 state를 참조하는 함수들 등.. 수많은 불순함수가 존재하는 FE 프로젝트에서,, 순수함수만으로 프로그래밍으로 한다는건 어불성설이다.. 하지만, 어차피 모두 순수함수로 만들자도 아니고, 단지 이둘을 구분해서 알아두고, 구분해서 개발하자는 취지로 알고 넘어가면 될 것 같다.

        • 참조 투명성 : 숨수함수를 정의하는 좀 더 공식적인 방법으로 위에서 설명한 것 과 같다.

profile
완벽함 보다는 최선의 결과를 위해 끊임없이 노력하는 개발자

0개의 댓글