Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

신나는 개발...

Functional Programming in JavaScript, Part 1: The Unit 본문

weekly

Functional Programming in JavaScript, Part 1: The Unit

벽돌1 2020. 4. 8. 21:26

https://marmelab.com/blog/2018/03/14/functional-programming-1-unit-of-code.html

 

Functional Programming in JavaScript, Part 1: The Unit

This gentle introduction to functional programming explains pure functions, currying, and composition. Easy peasy!

marmelab.com

A monad is just a monoid in the category(타입) of endofunctors.

아무것도 안하는 인자를 넘겼을 때 자기 자신이 튀어나온다면 monad

array.map(a => a) <- monad

 

monad, functor도 사실 디자인패턴이다.

 

Introduction To Functional Programming

Functional programming is a programming paradigm that is declarative. You describe what you want, as opposed to imperative programming(명령형), where you say how you want the computer to work.

fp는 선언적 프로그래밍 패러다임(<-> 명령형)이다. 너가 원하는 바를 명령형에 반대되게 설명한다.

너가 컴퓨터가 어떻게 돌기 원하는지 기술하는게 선언형 프로그래밍...

 

선언형 : control flow가 없다

명령형 : control flow(if, while, for...)가 있다

 

More precisely, in true functional programming, there is no control flow. No for, while, if, try, throw or catch statements. Functional Programming languages provide equivalents for these:

 

  • Instead of for or while, we use Array.map or Array.reduce,
  • Instead of if, we use ternary expressions condition? true statement : false statement,
  • And instead of throwing an error, we return it (error) as the result of a function.

즉 fp는 result를 리턴할 수 밖에 없다. 수학의 관점에는 exception이 없기 때문이다. 

순수하게 수학과 비슷해지려면 control flow가 없어야 한다.

result of function 을 error 대신 리턴했을 때 if없이 어떻게 처리하나여...?

이게 fp의 하이라이트

 

Also, functional programming forbids side effects. To be more precise, functional programming can have side effects, but they get isolated from the rest of the code. And in case you're wondering, a side effect is anything that modifies the state outside of the function,

또한, FP는 사이드이팩트를 금지한다, 더 자세히 설명하자면 FP는 사이드이팩트를 가질 수 ㅇ있다. 하지만 다른 코드들로부터 고립된다.

궁금하다면, 사이드이팩트는 (함수의) 바깥의 상태를 수정하는 어떤 것이다.

in case ~한다면 할께 근데 안하면 안 할꺼야, 이런 너낌 : 너가 궁금하다면, 정~~~~ 너가 궁금하면, 너가 궁금할 수 있으니까 말하는데,

 

for instance sending an HTTP request, changing the information displayed on a screen, recording a log, or calling a function which has such side effects. Yes, you need side effects all the time.

예를들어 HTTP requeset를 보내면 ~~~ 들이 사이트이팩트이다.

FP는 immutable가 목표다... 사이드 이팩트를 격리시켜서 테스트를 쉽게 mocking하기 쉽게 하자.

사이드이팩트는 언제나 필요하다... (이걸 분리해서 예측 가능하도록 만들자)

 

Lastly, functional programming enforces immutability. That means that a function should never modify its arguments. For instance, [1,2,3].push(4) mutates the initial array and returns the new length. On the other hand, [1,2,3].concat(4) returns a new array with the values [1,2,3,4]. It doesn't mutate the initial object.

concat => FP

Why Bother With Functional Programming?

Why not use side effects and mutate everything that we want? After all, we've been doing imperative programming for decades.

왜 사이드 이팩트를 사용하지 않고 우리가 원하는 모든걸 mutate하고 앉았니...? 결국 우리는 명령형 프로그래밍을 수십년동안 해오고있다.

 

The fact is, programming without side effects or mutations makes the code more predictable, and less error prone. It also makes it easy to run the code in parallel, and benefit from the gazillion CPUs sitting in today's computers (instead of just 1 core for a standard Node.js program for instance).

사실 사이트이팩트나 변경이 없는 프로그래밍은 

노드는 코어 하나만 제대로 사용할 수 있다.

(일반적 노드 프로그램의 단일 코어 대신에)

 

FP code is more predictable because the execution does not depend on the context. Functions never throw an error, and always return the same result given the same input.

항상 주어진 같은 input을 리턴한다. ---> 동일한 input에 동일한 output을 리턴한다.

 

var global = 0;
const square = x => {
  global++; // evil side effect
  return x * x;
};
const main = () => {
  var res = square(5);
  res += global;
  return res;
};

main(); // 26
main(); // 27

위 코드는 동일한 input에 동일한 output을 보장하지 않네유

 

Pure Functions

I will start with the basic, literally, by talking about the unit of code in functional programming, a.k.a. the function.

a.k.a the function  = the unit of code

 

In FP, functions have to be pure. A pure function has the following characteristics:

  • Its result depends only on its input
// impure
var changeResult = true;
const a => changeResult ? a + a : a;

// pure
const fn = (a, changeResult)  => changeResult ? a + a : a;

impurre는 input이 그대로여도 changeResult가 바뀌면 output이 바뀌기 때문에 pure하지 않습니당

 

  • It does not mutate any variable outside of its body (함수 바깥의 어떤 변수도 변하지 않는다)
// impure
var nbCall = 0;
const fn = () => {
  // do something
  nbCall++;
};

// pure
const fn = (arg, nbCall) => {
  // do something

  return {
    result,
    nbCall: nbCall++,
  };
};

 

  • It does not mutate its arguments
// impure ...? 자바스크립트로 테스트해보면 안바뀜...
const inc = i => i++;

// pure
const inc = i => i + 1;

 

  • It never throws an error

Throwing error break the flow of execution, and the function has no control as to where the error will be caught if it is indeed caught. Throwing an error is like throwing a bomb, in the hope that others will be able to handle it. And it forces users of the function to wrap it in a try/catch. Instead, you can gently return the error.

exception을 던지는 함수는 에러가 캐치되는 곳에 대하여 관여하지 않는다.

users of the function (함수의 클라이언트들)

// impure
const add = (a, b) => {
  if (typeof a !== "number" || typeof b !== "number") {
    throw new Error("add must take two numbers");
  }
  return a + b;
};

// pure
const add = (a, b) => {
  if (typeof a !== "number" || typeof b !== "number") {
    return { error: new Error("add must take two numbers") };
  }

  return { result: a + b };
};

You might already use pure functions without knowing it. The important thing to learn here is how to tell a pure from an impure function.

 

new Date() 마저도 순수함수가 아니다... 왜냐면 동일인풋 -> 아웃풋이 아니기 때문이야.

 

....이걸 FP는 어떻게 해처나갈것인가...?

 

Currying

N개의 인자를 받는 함수를 1개를 받는 함수 N개로 바꾸는 것임니당.....

 

You know what is even better than a pure function? A function that takes only one argument. It's better because it becomes a simple mapping from a value to another. Such a function can be replaced by an object:

const increment = v => v + 1;
increment(3); // 4

// could be replaced by : 동일인풋-동일아웃풋이라면 이것도 가능하지 않을까?
const increment = {
  1: 2,
  2: 3,
  3: 4,
};
increment[3]; // 4

 

프로그래밍이 상수화 되는 효과....

Wait, what? How am I supposed to do anything useful with a single argument? I cannot even do a simple add function.

 

function add(a) {
  return function(b) {
    return a + b;
  };
}
// or, using arrow functions
const add = a => b => a + b;

add(3)(5); // 8

This kind of function is called a curried function. The name comes from the mathematician Haskell Curry, who developed the concept (yes, the guy gave his name to this concept, and to a programming language. Badass!). With curried function, code that manipulates other functions know what to expect. A function always needs a single argument to executes.

This also allows for more generic code.

Let's say you need to send a greeting message to a lot of people.

 

const partial = add(1);

partial은 partial application이 됩니다...

커링은 application을 부분적으로 완성시킬 수 있도록 해줌

왜 상수화되었냐? 프로그램의 일부가 확정되었으니까 -> 생성 시점에 버그 가능성을 없앨 수 있다.

 

지난번 포스트 참고

Function Composition

One of the great benefits of functions that take only one parameter is composition.

const add5 = a => a + 5;
const double = a => a * 2;
const add5ThenMultiplyBy2 = a => double(add5(a));

add5ThenMultiplyBy2(1); // 12

When functions take only one argument, their calls can get chained (sort of), one taking the result of the other. But writing f1(f2(f3(...))) gets tiresome fast. Thankfully, we can create a helper to compose function calls for us. Let's call it compose.

함수가 하나의 인자만 취ㅣ한다면 그들ㄹ에 대한 호출들은 chain될 수 있다(뭐 그런 종류의), 하나가 다른 하나의 결과를 취하는 calls. 

tiresome 피곤한 handsome잘생긴 some이 붙으면 형용사 너낌

Let's call it compose : 그걸 compose라고 해보자.

const compose = (f1, f2) => arg => f1(f2(arg));

const add5ThenMultiplyBy2 = compose(
  double,
  add5
);
add5ThenMultiplyBy2(1); // 12

With compose, we end up writing the functions in the reverse order of their execution.

The reason for this order comes from mathematics, and is logic. Simply put, if we compose f1 with f2, we get f1(f2(x)), which reads "f1 of f2 of x". 

f2가 적용되고 f1이 적용된다.

 

 

사이드이팩트가 없고 immutable 하다면 pure function

compose -> single argument함수가 있다면 함수를 compose할 수 있다.

 

fp

control flow

imparrative, declartive

pure : depends on only input, no change others, 

curry : partiall application

composition : single argument