20200312 TIL

sangminnn·2020년 3월 12일
0

Functional Programming (함수형 프로그래밍)

→ 함수형 프로그래밍의 기본 전제는 순수함수로만 이루어져 있어야 한다는 것

Q. 순수함수란 ?

A. side effect(부수효과) 를 발생시키지 않는 함수. 즉. 원하는 결과를 return 하는것 이외의 역할을 수행하지 않는 함수를 말한다.

ex)

  • 변수 수정
  • 자료구조를 제자리에서 수정한다.
    • 저는 "이미 데이터가 할당된 컬렉션에 새로운 데이터로 할당한다" 정도로 이해하고 있습니다.
  • 객체의 필드를 설정한다.
  • 예외를 던지거나 오류를 내면서 실행을 중단한다.
  • 콘솔에 출력하거나 사용자의 입력을 받아들인다.
  • 화면에 그린다.

장점

→ 모듈성이 증가하며 그에 따라 테스트가 결국 쉬워지게 된다.

Side Effect 에 대해

→ 함수는 두가지 종류의 입력과 출력이 존재한다.

  1. 보통 우리가 알고있는 parameter, arguments 를 통해 인자를 받고, 그 인자를 가공해 값을 return 하는 입-출력 방식
  2. 인자도 없고 return 도 존재하지 않지만, 내부에서 변수, 함수를 선언할 때, 어떤 상태 혹은 값이 필요한 경우에 나타나는데 이때 내재되어있는 input - output에 대한 것이 있다.

이 두가지 종류의 I-O 중에서 2번째 I-O를 Side Effect 라고 부른다.

+++) 캡슐화는 구현 세부 사항을 숨기는 것에 관한 것이다. 코드의 내부를 숨겨서 호출하는 쪽에서 걱정할 필요가 없게 하는 것이 캡슐화의 목적.

그렇다면 어떻게 해야 순수함수화 할 수 있을지?

→ 알게모르게 사용하는 보이지않는 input을 아예 인자로 받고, return 값을 분명하게 정하는 과정을 통해 순수함수화 할 수 있다.

이렇게 할 경우의 단편적으로 봤을 때, 인자가 늘어나서 복잡성이 증가했다고 생각한다.

하지만 실제로는 해당 함수의 signature name, 그리고 인자를 더 직관적으로 확인하고 return을 더욱 합리적으로 추론할 수 있게된다. 이렇게 하면 Testing 과정에서도 더욱 도움이 된다.(?)

모든 입력이 입력으로 선언되고 (숨겨진 것이 없어야 한다) 마찬가지로 모든 출력이 출력으로 선언된 함수를 ‘순수(pure)’하다고 부른다.

순수하지 않은 코드를 ‘독립적으로’ 사용할 수는 없다. 독립적으로 테스트 할 수 없다. 테스트하거나 디버그가 필요할 때면 그것이 의존하고 있는 것을 항상 신경써야 한다.

함수형 프로그래밍(FP) vs 객체 지향형 프로그래밍(OOP)

OO의 대답은 “부작용을 ‘객체’라는 경계에 가두어라”이고, 반면 FP의 대답은 “부작용을 제거하라”이다.

클로저(Closure)

처음부터 클로져는 ‘시간’이라고 하는 한가지 특정 부작용을 잘 처리하도록 설계되어있다.

InboxQueue의 상태는 시간이 지남에 따라 변하는 값이다. 우리는 시간(Time)을 InboxQueue의 의미에 대한 부원인(side-cause)이라고 볼 수 있다.

  1. https://medium.com/@jooyunghan/함수형-프로그래밍이란-무엇인가-fab4e960d263
  2. https://medium.com/@jooyunghan/어떤-프로그래밍-언어들이-함수형인가-fec1e941c47f

JS primitive type - Number, String, Boolean, Null, Undefined, Symbol

변수라는 것은 사실 어떠한 값을 가질 때, 그 값을 가지고 있는게 아니라 메모리 상에서 그 값의 주소값을 가지고 있을 뿐이다.

다른 언어들에서는 int, long, float, double 와 같이 정수, 실수에 대한 type이 정해져 있지만, JS는 동적타입 언어로 어떤 값이 들어오던지 타입추론을 통해 값을 스스로 인지한다.

→ 따라서 number타입은 모든 정수, 실수들을 실수로만 인지하기때문에 type이 나누어져 있지 않다.

개인적 생각 ) 다른 언어들은 정적타입언어로 해당 타입을 정해주어야 그 값에 맞는 메모리를 할당해주어 메모리 낭비를 줄일 수 있지만, JS 는 동적타입언어로 숫자값은 모두 실수값으로 인지하기 때문에, 따로 type 설정이 필요하지 않다는 점에서 number라는 단일 type으로 만 존재한다.

+) 아래의 3가지 값도 number 타입에서 나올수 있는 값이다.

  • Infinity : 양의 무한대
  • -Infinity : 음의 무한대
  • NaN : 산술 연산 불가(not-a-number)

JS Number Type

*** Javascript는 Java와 달리 int, double 같은 숫자 타입이 나눠져 있지 않다.


Closure (poiema ver.)

함수 outerFunc는 내부함수 innerFunc를 반환하고 생을 마감했다. 즉, 함수 outerFunc는 실행된 이후 콜스택(실행 컨텍스트 스택)에서 제거되었으므로 함수 outerFunc의 변수 x 또한 더이상 유효하지 않게 되어 변수 x에 접근할 수 있는 방법은 달리 없어 보인다. 그러나 위 코드의 실행 결과는 변수 x의 값인 10이다. 이미 life-cycle이 종료되어 실행 컨텍스트 스택에서 제거된 함수 outerFunc의 지역변수 x가 다시 부활이라도 한 듯이 동작하고 있다. 뭔가 특별한 일이 일어나고 있는 것 같다.

이처럼 자신을 포함하고 있는 외부함수보다 내부함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부함수가 호출되더라도 외부함수의 지역 변수에 접근할 수 있는데 이러한 함수를 클로저(Closure)라고 부른다.

→ 클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.

클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수

→ 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수다라고 말할 수 있겠다.

클로저에 의해 참조되는 외부함수의 변수 즉 outerFunc 함수의 변수 x를 자유변수(Free variable)라고 부른다. 클로저라는 이름은 자유변수에 함수가 닫혀있다(closed)라는 의미로 의역하면 자유변수에 엮여있는 함수라는 뜻이다.

실행 컨텍스트의 관점에 설명하면, 내부함수가 유효한 상태에서 외부함수가 종료하여 외부함수의 실행 컨텍스트가 반환되어도, 외부함수 실행 컨텍스트 내의 활성 객체(Activation object)(변수, 함수 선언 등의 정보를 가지고 있다)는 내부함수에 의해 참조되는 한 유효하여 내부함수가 스코프 체인을 통해 참조할 수 있는 것을 의미한다.

내부함수가 외부함수에 있는 변수의 복사본이 아니라 실제 변수에 접근한다는 것에 주의

즉시실행함수는 함수를 반환하고 즉시 소멸

  1. 즉시실행함수는 함수를 반환하고 즉시 소멸한다. 즉시실행함수가 반환한 함수는 자신이 생성됐을 때의 렉시컬 환경(Lexical environment)에 속한 변수 isShow를 기억하는 클로저다. 클로저가 기억하는 변수 isShow는 box 요소의 표시 상태를 나타낸다.
  2. 클로저를 이벤트 핸들러로서 이벤트 프로퍼티에 할당했다. 이벤트 프로퍼티에서 이벤트 핸들러인 클로저를 제거하지 않는 한 클로저가 기억하는 렉시컬 환경의 변수 isShow는 소멸하지 않는다. 다시 말해 현재 상태를 기억한다.
  3. 버튼을 클릭하면 이벤트 프로퍼티에 할당한 이벤트 핸들러인 클로저가 호출된다. 이때 .box 요소의 표시 상태를 나타내는 변수 isShow의 값이 변경된다. 변수 isShow는 클로저에 의해 참조되고 있기 때문에 유효하며 자신의 변경된 최신 상태를 게속해서 유지한다.

→ 이처럼 클로저는 현재 상태(위 예제의 경우 .box 요소의 표시 상태를 나타내는 isShow 변수)를 기억하고 이 상태가 변경되어도 최신 상태를 유지해야 하는 상황에 매우 유용하다. 만약 자바스크립트에 클로저라는 기능이 없다면 상태를 유지하기 위해 전역 변수 를 사용할 수 밖에 없다.

전역 변수는 언제든지 누구나 접근할 수 있고 변경할 수 있기 때문에 많은 부작용을 유발해 오류의 원인이 되므로 사용을 억제해야 한다.

custom ) 클로저는 Execution Context의 개념 및 동작을 정확하게 이해하여 활용하는 방식으로, 전역 변수의 경우 사용은 쉬우나, 어디서든 누구든지 접근이 가능하기 때문에 오류의 원인이 된다는 점에서 사용을 지양해야 하기때문에, 그 대안으로 사용할 수 있다.

closure는 실행 컨텍스트의 관점으로 보았을 때, 내부 함수가 참조하는 외부 변수는 외부 변수가 존재하는 외부 함수가 반환되어 소멸되더라도 추후 내부 함수의 호출 시에 해당 외부 변수가 필요하다면 해당 주소에 대한 정보를 계속해서 가지고 있기 때문에 전역변수처럼 사용할 수 있는 지역 변수를 만들게 되는 것이다.

이렇게 함에 따라 전역변수같은 지역변수를 활용하여 변화하는 상태에 대한 값을 기억시켜 항상 최신 값을 가지고 있게 할 수 있다.

→ 단순히 함수를 통한 지역변수를 사용할 경우, 지역변수 counter를 0으로 초기화하기 때문에 언제나 1이 표시된다. 다시 말해 변경된 이전 상태를 기억하지 못한다.

→ 즉시실행함수는 한번만 실행되므로 increase가 호출될 때마다 변수 counter가 재차 초기화될 일은 없을 것이다.

for 루프의 초기문에서 사용된 변수의 스코프가 전역이 되기 때문에 발생하는 현상

poiemaweb 2.4 번 자주 발생하는 실수편

var arr = [];

for (var i = 0; i < 5; i++) {
  arr[i] = function () {
    return i;
  };
}

for (var j = 0; j < arr.length; j++) {
  console.log(arr[j]());
}

이 코드로는 0,1,2,3,4 가 출력되는것이 아니라 5가 5번 나타나게된다.

이 이유는 arr[i]에 대한 for문이 돌아갈 때, arr[0] 부터 arr[4] 까지 모두 function 이 들어가는데, 이 함수는 i에 대해 return 하는 함수이다. 하지만 여기서 i 는 존재하지 않게되고, 그 값은 자유변수 i에서 가지고 오기때문에 5번의 i는 모두 for문이 돌면서 5가 되어버린 i 값을 참조하게 되어 5가 5번 출력된다.

여기서 IIFE(즉시실행함수)를 사용할 경우나, let으로 i를 선언하는 경우에는 해당 key값에 i가 capture되어 그 순간의 i값이 원하는 대로 출력된다.

profile
생각하며 코딩하려고 노력하는 개발자가 되겠습니다.

0개의 댓글