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
관리 메뉴

신나는 개발...

Master the JavaScript Interview: What is Functional Programming? 본문

weekly

Master the JavaScript Interview: What is Functional Programming?

벽돌1 2020. 4. 10. 23:16

https://medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0

 

Master the JavaScript Interview: What is Functional Programming?

“Master the JavaScript Interview” is a series of posts designed to prepare candidates for common questions they are likely to encounter…

medium.com

“Master the JavaScript Interview” is a series of posts designed to prepare candidates for common questions they are likely to encounter when applying for a mid to senior-level JavaScript position. These are questions I frequently use in real interviews.

Functional programming has become a really hot topic in the JavaScript world. Just a few years ago, few JavaScript programmers even knew what functional programming is, but every large application codebase I’ve seen in the past 3 years makes heavy use of functional programming ideas.

few 부정

불과 몇년전만해도 fp가 뭔지 아는 개발자 조차 몇 없었다.

 

Functional programming (often abbreviated FP) is the process of building software by composing pure functions, avoiding shared state, mutable data, and side-effects. Functional programming is declarative rather than imperative, and application state flows through pure functions. Contrast with object oriented programming, where application state is usually shared and colocated with methods in objects.

FP는 순수함수를 조립하고 공유상태, 수정가능한 상태, 사이드이펙트를 피항여 소프트웨어를 빌딩하는 과정이다. FP는 명령적이라기보단 선언적이다. 그리고 어플리케이션의 상태는 순수함수를 따라 흐른다. 어플리케이션의 상태가 객체의 함수 내에서 공유되고 연관되어지는 OOP와 대조된다.

 

colocate : object와 함께 locate된다.. 함께 배치된다...

메소드와 상태가 같이 움직이는 너낌

rather then imperative 명령적이라기 보다는

메소드 근처에 상태가 있다는 것에 부정정이야...

 

 

Functional programming is a programming paradigm, meaning that it is a way of thinking about software construction based on some fundamental, defining principles (listed above). Other examples of programming paradigms include object oriented programming and procedural programming.

 

list above : fure function, mutable data...

defining = some fundamental...

FP는 위와같은 원칙이 정의된 근본적인 것에 근간을 둔 소프웨어 구조에 대해 생각할 수 있는 방법을 의미하는 프로그래밍 페러다임이다.

프로그래밍 패러다임의 예시로 객체지향과 절차지향이 있다.

 

Functional code tends to be more concise, more predictable, and easier to test than imperative or object oriented code — but if you’re unfamiliar with it and the common patterns associated with it, functional code can also seem a lot more dense, and the related literature can be impenetrable to newcomers.

함수형 코드는 명령형이나 OOP보다 간결하고 예측 가능하고 테스트하기 쉬운 경향이 있다.

하지만 당신이 FP와 이것과 관련된 공통 코드에 익숙하지 않다면 함수형코드는 더 빽빽해 보일거고 관련된 문장이나 글들은 뉴비들에게 감이 안 올수 있다. 

penetrable 관통하는

impenetrable 관통모태

 

If you start googling functional programming terms, you’re going to quickly hit a brick wall of academic lingo that can be very intimidating for beginners. To say it has a learning curve is a serious understatement. But if you’ve been programming in JavaScript for a while, chances are good that you’ve used a lot of functional programming concepts & utilities in your real software.

FP용어들을 구글링하기 시작했다면 뉴비들에게 매우 겁을 주고고 있는 academic한 용어의 벽에 부딛혔을것이다. 이게 learning curve를 가졌다고 말하기에는 점 말하기 껄끄럽다. 하지만 당신이 자바스크립트를 사용해오고 있다면 실제 코드에서 많은 FP개념과 유틸리티를 사용했을 가능성이 꽤 있다.

intimidate 협박하다 위협하다

 

Don’t let all the new words scare you away. It’s a lot easier than it sounds.

 

The hardest part is wrapping your head around all the unfamiliar vocabulary. There are a lot of ideas in the innocent looking definition above which all need to be understood before you can begin to grasp the meaning of functional programming:

가장 어려운 파트는 용어들이다. 위에서 기술한 순수해보이는 정의를 위해 많은 idea들이 있다. 그리고 이 정의들은 모두 FP를 시작하기 전에 이해해야 한다.

innocent looking definition 순수해보이는 정의들 위에 많은 idea들이 있다.

 

  • Pure functions
  • Function composition
  • Avoid shared state
  • Avoid mutating state
  • Avoid side effects

In other words, if you want to know what functional programming means in practice, you have to start with an understanding of those core concepts.

다른말로 실제 FP가 무엇인지 알고싶다면 이 핵심갸념들을 이해하자.

in practice 실제에서

 

A pure function is a function which:

  • Given the same inputs, always returns the same output, and
  • Has no side-effects

Pure functions have lots of properties that are important in functional programming, including referential transparency (you can replace a function call with its resulting value without changing the meaning of the program). Read “What is a Pure Function?” for more details.

순수함수는 참조투명성을 포함한 FP에서 중요한 많은 프로퍼티를 가진다.

참조투명성이란 프로그램 의미에 어떤 변화도 없이 함수의 호출을 그 함수의 리턴값으로 바꿀 수 있다는 것이다.

 

referential transparency: 참조투명성 => function 호출을 의미의 변화 없이 출력값을로 바꿀수 있다.

 

Function composition is the process of combining two or more functions in order to produce a new function or perform some computation. For example, the composition f . g (the dot means “composed with”) is equivalent to f(g(x)) in JavaScript. Understanding function composition is an important step towards understanding how software is constructed using the functional programming. Read “What is Function Composition?” for more.

FP는 새로운 함수를 생산하거나 어떤 계산을 수행하기 위해 2개 이상의 함수를 조합하는 방식이다. 예를들어 자바스크립트에서 조합 f.g 는 f(g(x))와 동등하다. 함수조립을 이해하는건 FP를 이용하여 어떻게 소프트웨어가 설계되었는지 이해하는데에 매우 중요한 과정이다.

 

Shared State

Shared state is any variable, object, or memory space that exists in a shared scope, or as the property of an object being passed between scopes. A shared scope can include global scope or closure scopes. Often, in object oriented programming, objects are shared between scopes by adding properties to other objects.

공유된 상태는 공유된 스코프 안에서 변수 객체 메모리스페이스가 될 수 있다. 또는 스코프 사이에 넘겨진 오브젝트 프로퍼티나...

 

being stupid -> 원래는 안 멍청한데 순간 멍청한 너낌....

transaction을 토해내면서 채워달란소리 안하지, 걍 채워서 넘기지 -> 이게 shared state가 없는 상태야...

 

For example, a computer game might have a master game object, with characters and game items stored as properties owned by that object. Functional programming avoids shared state — instead relying on immutable data structures and pure calculations to derive new data from existing data. For more details on how functional software might handle application state, see “10 Tips for Better Redux Architecture”.

relying on 의존하다

대신에, 불변의 데이터 구조와 순수 계산에 의존한다

 

The problem with shared state is that (in order to understand the effects of a function,) you have to know the entire history of every shared variable that the function uses or affects.

하나의 함수의 영향도를 이해하기 위해서 전체 히스토리를 알아야한다.

 

Imagine you have a user object which needs saving. Your saveUser() function makes a request to an API on the server. While that’s happening, the user changes their profile picture with updateAvatar() and triggers another saveUser() request. On save, the server sends back a canonical user object that should replace whatever is in memory in order to sync up with changes that happen on the server or in response to other API calls.

서버는 user객첼를 클라이언트로 다시 보낼거야, 이 객체는 어떤상태이던간에 서버에서 이리ㅓㅇ난변경과함께 싱ㅇ크를 맞추기위해서... 혹은 다른 api콜..?

메모리에 있는 게 무엇이든 간에 싱크업하기 위해서 바꿔야한다.

 

Unfortunately, the second response gets received before the first response, so when the first (now outdated) response gets returned, the new profile pic gets wiped out in memory and replaced with the old one. This is an example of a race condition — a very common bug associated with shared state.

 

Another common problem associated with shared state is that changing the order in which functions are called can cause a cascade of failures because functions which act on shared state are timing dependent:

단계적 실패를 야기할 수 있다.

아래 예제는 호출 순서에 의존적임 -> 순서에 따라 다른 결과가 나옴...

 

// With shared state, the order in which function calls are made
// changes the result of the function calls.
const x = {
  val: 2
};

const x1 = () => x.val += 1;

const x2 = () => x.val *= 2;

x1();
x2();

console.log(x.val); // 6

// This example is exactly equivalent to the above, except...
const y = {
  val: 2
};

const y1 = () => y.val += 1;

const y2 = () => y.val *= 2;

// ...the order of the function calls is reversed...
y2();
y1();

// ... which changes the resulting value:
console.log(y.val); // 5

When you avoid shared state, the timing and order of function calls don’t change the result of calling the function. With pure functions, given the same input, you’ll always get the same output. This makes function calls completely independent of other function calls, which can radically simplify changes and refactoring. A change in one function, or the timing of a function call won’t ripple out and break other parts of the program.

given the same input 같은 인풋이 주어졌을 때

which can radically simplify changes and refactoring. 앞문장 전체를 꾸며줌

이 말의 의미는 변경와 맆팩토링을 철저하게 단순화할 수 있다는 것이다

ripple, break 동급 둘 다 won't할거임

 

아래는 여러번 호출이나 순서를 막해도 결과가 바뀌지 않는다는 내용

const x = {
  val: 2
};

const x1 = x => Object.assign({}, x, { val: x.val + 1});

const x2 = x => Object.assign({}, x, { val: x.val * 2});

console.log(x1(x2(x)).val); // 5


const y = {
  val: 2
};

// Since there are no dependencies on outside variables,
// we don't need different functions to operate on different
// variables.

// this space intentionally left blank


// Because the functions don't mutate, you can call these
// functions as many times as you want, in any order, 
// without changing the result of other function calls.
x2(y);
x1(y);

console.log(x1(x2(y)).val); // 5

In the example above, we use Object.assign() and pass in an empty object as the first parameter to copy the properties of x instead of mutating it in place. In this case, it would have been equivalent to simply create a new object from scratch, without Object.assign(), but this is a common pattern in JavaScript to create copies of existing state instead of using mutations, which we demonstrated in the first example.

from scratch 밑글림에서... 그냥 평범하게 만드는 뭐 그런뜻인까...? ㅇ여튼 Object.assign없이 생성하는거랑 동등할거야

 

 

If you look closely at the console.log() statements in this example, you should notice something I’ve mentioned already: function composition. Recall from earlier, function composition looks like this: f(g(x)). In this case, we replace f() and g() with x1() and x2() for the composition: x1 . x2.

 

Of course, if you change the order of the composition, the output will change. Order of operations still matters. f(g(x)) is not always equal to g(f(x)), but what doesn’t matter anymore is what happens to variables outside the function — and that’s a big deal. With impure functions, it’s impossible to fully understand what a function does unless you know the entire history of every variable that the function uses or affects.

 

Remove function call timing dependency, and you eliminate an entire class of potential bugs.

 

 

Immutability

An immutable object is an object that can’t be modified after it’s created. Conversely, a mutable object is any object which can be modified after it’s created.

 

Immutability is a central concept of functional programming because without it, the data flow in your program is lossy. State history is abandoned, and strange bugs can creep into your software. For more on the significance of immutability, see “The Dao of Immutability.”

signficance : 중요성

 

In JavaScript, it’s important not to confuse const, with immutability. const creates a variable name binding which can’t be reassigned after creation. const does not create immutable objects. You can’t change the object that the binding refers to, but you can still change the properties of the object, which means that bindings created with const are mutable, not immutable.

Immutable objects can’t be changed at all. You can make a value truly immutable by deep freezing the object. JavaScript has a method that freezes an object one-level deep:

name binding -> 할당은 오브젝트와 이름을 바인딩하는 것

binding refers to 

const는 그냥 할당되고 다시 할당할 수 없다는 거지 immutable한 오브젝트를 생성하는게 아니다.

 

Immutable objects can’t be changed at all. You can make a value truly immutable by deep freezing the object. JavaScript has a method that freezes an object one-level deep:

const a = Object.freeze({
  foo: 'Hello',
  bar: 'world',
  baz: '!'
});

a.foo = 'Goodbye';
// Error: Cannot assign to read only property 'foo' of object Object

But frozen objects are only superficially immutable. For example, the following object is mutable:

const a = Object.freeze({
  foo: { greeting: 'Hello' },
  bar: 'world',
  baz: '!'
});

a.foo.greeting = 'Goodbye';

console.log(`${ a.foo.greeting }, ${ a.bar }${a.baz}`);

As you can see, the top level primitive properties of a frozen object can’t change, but any property which is also an object (including arrays, etc…) can still be mutated — so even frozen objects are not immutable unless you walk the whole object tree and freeze every object property.

다 탐색하고 모든 프로퍼티를 얼리지 않는 한,

 

In many functional programming languages, there are special immutable data structures called trie data structures (pronounced “tree”) which are effectively deep frozen — meaning that no property can change, regardless of the level of the property in the object hierarchy.

 

 

Tries use structural sharing to share reference memory locations for all the parts of the object which are unchanged after an object has been copied by an operator, which uses less memory, and enables significant performance improvements for some kinds of operations.

freeze하면 복사할 필요없이 참조를 그냥 공유하면 되잖아 -> 카피할 때 name이 원본 object에 binding되는 것

 

For example, you can use identity comparisons at the root of an object tree for comparisons. If the identity is the same, you don’t have to walk the whole tree checking for differences.

identity comparisons 동등비교

walk the whole tree 완전히 탐색하다

 

There are several libraries in JavaScript which take advantage of tries, including Immutable.js and Mori.

I have experimented with both, and tend to use Immutable.js in large projects that require significant amounts of immutable state. For more on that, see “10 Tips for Better Redux Architecture”.

 

Side Effects

A side effect is any application state change that is observable outside the called function other than its return value. Side effects include:

  • Modifying any external variable or object property (e.g., a global variable, or a variable in the parent function scope chain)
  • Logging to the console
  • Writing to the screen
  • Writing to a file
  • Writing to the network
  • Triggering any external process
  • Calling any other functions with side-effects

Side effects are mostly avoided in functional programming, which makes the effects of a program much easier to understand, and much easier to test.

Haskell and other functional languages frequently isolate and encapsulate side effects from pure functions using monads. The topic of monads is deep enough to write a book on, so we’ll save that for later.

What you do need to know right now is that side-effect actions need to be isolated from the rest of your software. If you keep your side effects separate from the rest of your program logic, your software will be much easier to extend, refactor, debug, test, and maintain.

This is the reason that most front-end frameworks encourage users to manage state and component rendering in separate, loosely coupled modules.

 

higher order function -> 함수를 인잘로 받거나 함수를 리턴하는 것

 

 

Reusability Through Higher Order Functions

Functional programming tends to reuse a common set of functional utilities to process data. Object oriented programming tends to colocate methods and data in objects. Those colocated methods can only operate on the type of data they were designed to operate on, and often only the data contained in that specific object instance.

 

In functional programming, any type of data is fair game. The same map() utility can map over objects, strings, numbers, or any other data type because it takes a function as an argument which appropriately handles the given data type. FP pulls off its generic utility trickery using higher order functions.

 

JavaScript has first class functions, which allows us to treat functions as data — assign them to variables, pass them to other functions, return them from functions, etc…

변수에 할당할 수 있다면 first class 

변수에 할당할 수 있다면 인자, 리턴값 다 될 수 있다.

 

A higher order function is any function which takes a function as an argument, returns a function, or both. Higher order functions are often used to:

  • Abstract or isolate actions, effects, or async flow control using callback functions, promises, monads, etc…
    • Abstract 구체적으로 뭔지 모르고 추상적으로 알아.... 리턴되거나 인자로 넘어온 function이 뭔지...
  • Create utilities which can act on a wide variety of data types
  • Partially apply a function to its arguments or create a curried function for the purpose of reuse or function composition
  • Take a list of functions and return some composition of those input functions

 

Containers, Functors, Lists, and Streams

A functor is something that can be mapped over. In other words, it’s a container which has an interface which can be used to apply a function to the values inside it. When you see the word functor, you should think “mappable”.

Array.map()이 가능하므로 array는 mapped over되는 객체. (사상될 수 있는)

 

Earlier we learned that the same map() utility can act on a variety of data types. It does that by lifting the mapping operation to work with a functor API. The important flow control operations used by map() take advantage of that interface. In the case of Array.prototype.map(), the container is an array, but other data structures can be functors, too — as long as they supply the mapping API.

mapping operation을 lifting함으로서 같ㅇ이ㅣ ㅇ일하기 위해서 -> mapping할 수 있는 인자를 넘김으로서

by lifting === by passing

 

Let’s look at how Array.prototype.map() allows you to abstract the data type from the mapping utility to make map() usable with any data type. We’ll create a simple double() mapping that simply multiplies any passed in values by 2:

make map usable 맵을 사용가능하게 만든다

any passed in values: 전달된 밸ㄹ류들

2배로 간단하게 곱하는

 

What if we want to operate on targets in a game to double the number of points they award? All we have to do is make a subtle change to the double() function that we pass into map(), and everything still works:

 

“A list expressed over time is a stream.”
유저가 클릭이벤트가 리스트라면... 클ㄹ리ㅣㄱ을 기다렬/ㅆ다가 이벤트가 있으니까..... map이 기다렸다가 실행되니까
이게 rxjs의 기본

All you need to understand for now is that arrays and functors are not the only way this concept of containers and values in containers applies. For example, an array is just a list of things. A list expressed over time is a stream — so you can apply the same kinds of utilities to process streams of incoming events — something that you’ll see a lot when you start building real software with FP.

이 개념이 적용되는 유일한 방법이 아니다.

 

Declarative(flow control X) vs Imperative(fllow control O)

Functional programming is a declarative paradigm, meaning that the program logic is expressed without explicitly describing the flow control.

for루프 안 쓰니까 비용이 준다는 얘기

 

Imperative programs spend lines of code describing the specific steps used to achieve the desired results — the flow control: How to do things.

 

Declarative programs abstract the flow control process, and instead spend lines of code describing the data flow: What to do. The how gets abstracted away.

get abstracted away 점점 추상화되어서 구체적으로 설명할 필요가 없는 너낌

abstract 추상적이지 구체적 x

 

For example, this imperative mapping takes an array of numbers and returns a new array with each number multiplied by 2:

 

const doubleMap = numbers => {
  const doubled = [];
  for (let i = 0; i < numbers.length; i++) {
    doubled.push(numbers[i] * 2);
  }
  return doubled;
};

console.log(doubleMap([2, 3, 4])); // [4, 6, 8]

 

 

This declarative mapping does the same thing, but abstracts the flow control away using the functional Array.prototype.map() utility, which allows you to more clearly express the flow of data:

const doubleMap = numbers => numbers.map(n => n * 2);

console.log(doubleMap([2, 3, 4])); // [4, 6, 8]

 

Imperative code frequently utilizes statements. A statement is a piece of code which performs some action. Examples of commonly used statements include for, if, switch, throw, etc…

 

Declarative code relies more on expressions. An expression is a piece of code which evaluates to some value. Expressions are usually some combination of function calls, values, and operators which are evaluated to produce the resulting value.

These are all examples of expressions:

 

flow control이 아닌게 함수, value, operator의 호출...

 

2 * 2
doubleMap([2, 3, 4])
Math.max(4, 3, 2)

 

Usually in code, you’ll see expressions being assigned to an identifier, returned from functions, or passed into a function. Before being assigned, returned, or passed, the expression is first evaluated, and the resulting value is used.

evaluated

변수가 된다 -> 평가된다

 

Conclusion

Functional programming favors:

  • Pure functions instead of shared state & side effects
  • Immutability over mutable data
  • Function composition over imperative flow control
  • Lots of generic, reusable utilities that use higher order functions to act on many data types instead of methods that only operate on their colocated data
    • person의  getOld가 다른 객체에는 사용될 수 없다는 식으로 말하는건데 사실 oop에서도 age를 injection받으면 가능하쥬
    • 개념적으로 너무 떨어진 거지만 중복된거면 걍 공유하자는 것이 FP
    • oop는 개념적으로 묶자
  • Declarative rather than imperative code (what to do, rather than how to do it)
  • Expressions over statements
  • Containers & higher order functions over ad-hoc polymorphism

 

why -> what -> how 순으로 생각하자....