티스토리 뷰
함수형 프로그래밍이란?
- 함수형 프로그래밍(functional programming)은 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나.
- 상태, 가변 데이터를 지양.
- 상태의 변경이 아닌 함수의 응용을 통해 프로그래밍.
- 절차의 기술이 아닌 선언적으로 기술되는 선언형 프로그래밍 패러다임을 따름.
- 아래의 내용으로 보통 정의내림
- FP is about pulling programs apart and reassembling them from the same parts, composing in functions together and that means we need to make the output of a function to serve as the input of the next one, in order to do so, we should avoid shared mutable state & side-effects (use pure functions)
- FP is about pulling programs apart and reassembling them from the same parts, composing in functions together and that means we need to make the output of a function to serve as the input of the next one, in order to do so, we should avoid shared mutable state & side-effects (use pure functions)
함수형 프로그래밍의 역사
- 알론조 처치가 1930년대에 개발한 람다 대수에서 함수에 대한 이론적 기반 태동.
- 1930년대에 계산가능성, 결정문제, 함수정의, 함수응용과 재귀를 연구하기 위해 람다 대수를 이용.
- 최초 함수형 프로그래밍 언어 IPL을 개발.
- 이후 훨씬 향상된 함수형 프로그래밍 언어인 리스프가 개발됨.
- 리스프는 현대적 함수형 프로그래밍의 여러 특징을 가짐
- 1980년대에 그동안의 함수형 프로그래밍에 대한 연구를 바탕으로 순수 함수형 언어인 하스켈 탄생.
- 최근 2000년대부터 cpu의 단일 코어 처리 한계가 찾아오고 멀티 코어 체제로 전환되면서 함수형 프로그래밍이 가진 특징을 적용해보고자 하는 수요가 증가.
함수형 프로그래밍 특징
- 함수는 절차에 집중하지 않고 결과에 집중.
- 무엇이 계산될지에만 강점을 둠.
- 데이터는 불변형.
- 문제를 여러 함수들로 분해하여 다룸.
- 계산을 위해 재귀와 조건절을 사용하는 수학 함수 개념에 기초.
- 루프 문과 같은 반복문과 조건문은 지원하지 않음. → if else가 있어 혼동될 수 있으나 함수형에서 이는 표현식의 일종이다(=함수)/loop는 재귀로 대체된다.
- ETC(무한 개념의 표현 등)
함수형 언어 종류
- Haskell
- SML
- Clojure
- Scala
- Erlang
- Clean
- F#
- ML/OCaml Lisp / Scheme
- XSLT
- SQL
- Mathematica
기본적인 함수형 프로그래밍에서 알면 도움될 용어와 개념
- Immutable Data(불면 데이터)
- Closure(클로저)
- First-class function(1급 함수)
- Maintainability(유지보수성)
- Modularity(모듈성)
- Memoization(메모이제이션)
- High order function(고차함수)
- Referential transparency(참조 투명성)
- Pure function(순수 함수)
- Currying
- curried function
- unary function
- partial application
- Point free Style
- lambda
- Fold(Left/Right)
- Monad
- Arity
- Auto Currying
- Function composition
- Continuation
- Purity
- Side effects
- Idempotent
- Predicate
- Consumer
- Function
- Supplier
- Contracts
- Category
- Value
- Constant
- Functor
- Pointed Functor
- Lift
- Equational Reasoning
- Lamda Calculus
- Lazy evaluation
- Monoid
- Comonad
- Applicative Functor
- Morphism
- Endomorphism
- Isomorphism
- Setoid
- Semigroup
- Foldable
- Lens
- Type Signatures
- Algebraic data type
- Sum type
- Product type
- Option
- Reactive Function Programming
- Reactive Functional Programming
- Declarative/Functdional vs Imperative Procedural
- Optics
- Reflection
- Tail call optimization
Immutable Data
- 데이터는 불변한다. 즉 이미 존재하는 데이터를 변경하는 것이 아닌, 데이터 구조를 생성만 할 수 있다. 그리고 변경하지 못한다.
- 원본 접기
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// Mutating your data - you lost your state
let source = ["h1", "h2", "h3"];
source[2] = "h4";
console.log("source");
console.log(source); // ["h1", "h2", "h4"]
//No mutation
source = ["h1", "h2", "h3"];
let dest = source.map(data => {
return data == "h3" ? "h4" : data;
});
console.log("source");
console.log(source); // ["h1", "h2", "h3"]
console.log("result");
console.log(dest); // ["h1", "h2", "h4"]
참조투명성(Referential transparency)
- 프로그램 동작 결과에 무관하게 관련 값을 대체할 수 있는 것.
- 원본 접기
-
1 const greet = () => 'Hello World!'
Closure(클로저)
- 정의가 매우 다양하다.
- 함수가 선언될 당시의 환경(environment)을 기억했다가 나중에 호출되었을때 원래의 환경에 따라 수행되는 함수
- 정적 스코프가 지정된 바인딩을 구현하는 기술로 이것은 환경과 실행코드(함수)를 표현
- 범위 외부의 변수에 액세스하는 방법
- 실행이 정의된 블록 밖으로 이동한 후에도 함수의 로컬변수를 액세스용으로 캡처하는 범위 등
First class function
- 컴퓨터 프로그래밍 언어 디자인에서 일급 객체(First class Object)라 함은, 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 의미.
- 즉, 언어 내부에서 값으로 표현되고 전달될 수 있는 자료형 또는 대상을 일급 객체라고 부름.
- 구체적으로는
- 함수의 실제 매개변수가 될 수 있어야 한다.
- 함수의 반환 값이 될 수 있어야 한다.
- 할당 명령문의 대상이 될 수 있어야 한다.
- 동일 비교의 대상이 될 수 있어야 한다.
- 런타임에 함수를 생성할 수 있어야 한다.
- 함수형 프로그래밍에서 함수는 위 조건을 충족한다.
Maintainability
- 함수형 패러다임을 통해 개발된 로직은 유지보수하기 쉽다는 내용을 어필하기 위한 용어.
- 단순하게 말해 사이드 이펙트가 없으니 수정할 때 두려움이 덜하다는 뜻.
Modularity
- 문제가 함수 단위로 분해되어 수행되어 모듈 식 설계를 이룰 수 있음을 뜻함.
- 단위 테스트 및 디버깅에 소요되는 시간을 줄여줌.
Memoization
- 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술
- 동일한 계산을 반복해야 할 경우 한 번 계산한 결과를 메모리에 저장해 두었다가 꺼내 씀으로써 중복 계산을 방지할 수 있게 하는 기법
- 동적 계획법 알고리즘에서 많이 본 개념
- 순수 함수 특징으로 같이 나타나는 특징
High order function
- 함수가 일급 객체로 취급될 때, 함수를 인자로 받거나 결과로 반환하는 함수. 수학에서의 연산자, 범함수와 맥락이 통용된다.
- 이를 제외한 함수는 일차 함수(first order function).
- 원본 접기
-
1
2
3const filter = (predicate, xs) => xs.filter(predicate)
const is = (type) => (x) => Object(x) instanceof type
filter(is(Number), [0, '1', 2, null]) // [0, 2]
Pure function
- 동일한 입력값이 주어지면 항상 동일한 결과값을 반환하는 함수
- 부작용이 발생하지 않음(불변).
- 원본 접기
-
1
2
3const greet = (name) => `Hi, ${name}`
greet('Brianne') // 'Hi, Brianne'
Currying
- 여러 개의 인자를 가진 함수를 호출 할 경우, 파라미터의 수보다 적은 수의 파라미터를 인자로 받으면 누락된 파라미터를 인자로 받는 기법.
- 다중 인수를 갖는 함수를 단일 인수를 갖는 함수들의 함수열로 바꾸는 것.
- 커링 구현해보기 정리글(JS)
- 원본 접기
-
1
2
3
4
5
6
7
8
9const sum = (a, b) => a + b
const curriedSum = (a) => (b) => a + b
curriedSum(40)(2) // 42.
const add2 = curriedSum(2) // (b) => 2 + b
add2(10) // 12
Curried function
- Currying의 결과물 함수
- 원본 접기
-
1
2
3
4
5
6
7
8// Curry function
// add = argument1 => argument 2 => Number
const add = arg1 => arg2 => arg1 + arg2;
// Partial Application
const addWithTen = add(10);
console.log(addWithTen(5)); // 15
Unary function(단항 함수)
- 단일 인자를 가지는 함수. 커리된 함수(Curried function)는 항상 이 단항 함수를 리턴.
Partial application(부분 적용)
- 커리된 함수를 통해 인수의 일부의 값이 확정된 함수.
- 값의 고정이 이뤄지는 경우 클로저를 사용하는 경우도 존재.
- 원본 접기
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 부분적으로 적용된 함수를 만드는 도우미
// 함수와 몇 가지 인수를 취합니다.
const partial = (f, ...args) =>
// 나머지 인수를 취하는 함수를 반환합니다.
(...moreArgs) =>
// 그리고 모두 원래 함수를 호출합니다.
f(...args, ...moreArgs)
// 뭔가 적용할 것
const add3 = (a, b, c) => a + b + c
// `2`와`3`을 부분적으로 `add3`에 적용하면 하나의 인자로 쓸 수 있습니다
const fivePlus = partial(add3, 2, 3) // (c) => 2 + 3 + c
fivePlus(4) // 9
Point free Style or Point free function
- 함수 정의에 인수를 생략하여 함수를 기술하는 함수 작성 방법.
- 커링(Curring) 또는 다른 고차 함수를 필요로 함.
- 불필요하게 인수를 기술하여 코드가 길어지는 것을 방지하고 간셜성과 가독성을 높이는 데 사용.
- 원본 접기
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const samples = Array.from({ length: 10 }, (value, index) => index + 1);
const double = a => a * a;
const isEven = a => a % 2 === 0;
samples
.map(double) // point free style
.filter(d => isEven(d));
// corner case - where signature mismatch happens
// parseInt => ( string, radix) => Number
samples.map(parseInt); // [ 1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 9]
// unary helper function - which helps to stream line the signature of the function
const unary = fn => first => fn.call(this, first);
samples.map(unary(parseInt)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - 원본 접기
-
1
2
3
4
5
6
7
8
9
10
11// Given
const map = (fn) => (list) => list.map(fn)
const add = (a) => (b) => a + b
// Then
// Not points-free - `numbers` is an explicit argument
const incrementAll = (numbers) => map(add(1))(numbers)
// Points-free - The list is an implicit argument
const incrementAll2 = map(add(1))
Lambda
- 값처럼 취급할 수 있는 익명의 함수
- 이름 없는 함수
- 원본 접기
-
1
2
3
4
5;(function (a) {
return a + 1
})
;(a) => a + 1
Auto Currying
- 다항 함수(여러 개의 인수를 취하는 함수)에 필요한 인자 수보다 적은 인자를 전달하면 자동으로 남은 수의 인자를 취하는 함수가 반환되도록 하는 기술.
- 원본 접기
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23const add = (x, y) => x + y
const curriedAdd = _.curry(add)
curriedAdd(1, 2) // 3
curriedAdd(1) // (y) => 1 + y
curriedAdd(1)(2) // 3
export function curry<A1, R>(fn: (arg1: A1) => R): (arg1: A1) => R;
export function curry<A1, A2, R>(fn: (arg1: A1, arg2: A2) => R): (arg1: A1) => (arg2: A2) => R;
export function curry<T extends (...args: any) => any>(fn: T): (...args: any[]) => any {
return function(...firstArgs) {
const argsLen = fn.length;
let allArgs = [];
return nextArgument(...firstArgs);
function nextArgument(...args) {
allArgs = allArgs.concat(args);
return allArgs.length >= argsLen ? fn.apply(this, allArgs) : nextArgument;
}
};
}
Function composition
- 한 함수의 출력이 다른 함수의 입력이 되도록 함수를 조합하는 기법.
- 원본 접기
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// composing functions
const pipe = (...fns) => initData => fns.reduce((arg, fn) => fn(arg), initData);
const nameInCaps = name => name.toUpperCase();
const reverseName = name =>
name
.split("")
.reverse()
.join("");
const first13Char = name => name.substring(0, 13);
const greet = name => `${name} to all!!!`;
const outputFn = pipe(
reverseName,
first13Char,
nameInCaps,
greet
)("!!!dnekeew yppah");
console.log(outputFn); // 'HAPPY WEEKEND to all!!!' - 원본 접기
-
1
2
3const compose = (f, g) => (a) => f(g(a)) // 정의
const floorAndToString = compose((val) => val.toString(), Math.floor) // 사용법
floorAndToString(121.212121) // '121'
Continuation(계속)
- 함수의 특정 지점에서 코드의 실행이 아직 진행되지 않은 부분
- 비동기 처리 시 일종의 콜백 영역
Coroutine 과 같은 맥락에서 실행의 흐름이 전이하는 경우 각 코루틴의 남은 실행 영역Generator 와 같은 영역에서 yield를 통해 코드 흐름이 중지/분기한 이후 지점
원본 접기-
1
2
3
4
5
6
7
8const printAsString = (num) => console.log(`Given ${num}`)
const addOneAndContinue = (num, cc) => {
const result = num + 1
cc(result)
}
addOneAndContinue(2, printAsString) // 'Given 3'
Side effects(부수 효과)
- 함수가 외부 변경 가능 상태와 상호작용하는 일체.
- 시스템 혹은 프로그램의 상태를 바꾸거나 외부 세계와 상호작용하는 행위.
- 외부 변수를 변경(전역 변수나 부모 함수 스코프(영역)에 있는 변수 값의 변경)
- 콘솔에 로깅하기
- 화면에 데이터를 출력하기
- 파일 IO 작업
- 네트워크 요청 보내기
- 외부 시스템 호출하기
- 부수 효과가 있는 함수(비순수 함수) 호출하기. 예) new Date() 사용
- DOM 트리 변경하기
- DB 접근하기 등
- 함수형 프로그래밍에서는 이러한 부수 효과가 필요한 경우에 대해 일종의 트릭을 사용함
- 원본 접기
-
1 console.log('IO is a side effect!')
Idempotent(멱등성)
- 함수를 수행한 결과를 다시 그 함수에 적용해도 다른 결과가 나오지 않는다면 그 함수는 멱등성을 가짐
- 원본 접기
-
1
2
3f(f(x)) ≍ f(x)
Math.abs(Math.abs(10))
sort(sort(sort([2, 1])))
Predicate(술부)
- 주어진 인자에 대해 참, 거짓을 리턴하는 함수.
- 원본 접기
-
1
2
3
4const predicate = (a) => a > 2;
[1, 2, 3, 4].filter(predicate) // [3, 4]
목록 출처
참고중인 자료
- https://gist.github.com/mkuklis/5294248
- https://www.guru99.com/functional-programming-tutorial.html
- https://whatisthenext.tistory.com/111
- https://ko.wikipedia.org/wiki/%EA%B3%A0%EC%B0%A8_%ED%95%A8%EC%88%98
- https://en.wikipedia.org/wiki/Functional_programming
- https://degoes.net/articles/fp-glossary
- 도서 :
- http://book.interpark.com/product/BookDisplay.do?_method=detail&sc.prdNo=272822398&gclid=CjwKCAiA9bmABhBbEiwASb35V62b-YG_t5wpCRVZO8Av7DloUK8QxBFDGx6fG5XIY_ou-pfWKFsc-xoCNoYQAvD_BwE
- http://book.interpark.com/product/BookDisplay.do?_method=detail&sc.prdNo=236756536&gclid=CjwKCAiA9bmABhBbEiwASb35V3zNQLwCDC27WkK6OYMvOIU7QkGGI_qgRu9YQ-9HBo3ZJ5DJL27qQBoC6HgQAvD_BwE
- http://book.interpark.com/product/BookDisplay.do?_method=detail&sc.prdNo=242319472&gclid=CjwKCAiA9bmABhBbEiwASb35VwMATTA29vG2kOR4ouyeDQQB5PrnJZr1lDZhpILhVBiJz8PMVy30LBoCML8QAvD_BwE
- http://book.interpark.com/product/BookDisplay.do?_method=detail&sc.prdNo=213915436&gclid=CjwKCAiA9bmABhBbEiwASb35VyO0K3clFsuBsKjd6kcnu0N4Gc6_3IQkIPXB63ThhsU4h_3P2PTtwBoCBtIQAvD_BwE
- http://book.interpark.com/product/BookDisplay.do?_method=detail&sc.prdNo=255364260&gclid=CjwKCAiA9bmABhBbEiwASb35V7lKDuVd7LwS3xhgPcgpYlk2n46Jt_hMdxQoRKWEjbF3pyrjaRp1LBoCW64QAvD_BwE
댓글