All Projects โ†’ gwanhyeong2 โ†’ functional-programming-jargon

gwanhyeong2 / functional-programming-jargon

Licence: MIT license
Jargon from the functional programming world in simple terms!

Projects that are alternatives of or similar to functional-programming-jargon

Fs2
Compositional, streaming I/O library for Scala
Stars: โœญ 1,998 (+1802.86%)
Mutual labels:  fp
Pratica
๐Ÿฅƒ Functional Algebraic Data Types
Stars: โœญ 246 (+134.29%)
Mutual labels:  fp
elm-antd
The official Ant Design UI Kit for Elm
Stars: โœญ 56 (-46.67%)
Mutual labels:  fp
Nyaya
Random Data Generation and/or Property Testing in Scala & Scala.JS.
Stars: โœญ 165 (+57.14%)
Mutual labels:  fp
Zio Saga
Purely Functional Transaction Management In Scala With ZIO
Stars: โœญ 200 (+90.48%)
Mutual labels:  fp
Design-Patterns
Project for learning and discuss about design patterns
Stars: โœญ 16 (-84.76%)
Mutual labels:  fp
Doobie
Functional JDBC layer for Scala.
Stars: โœญ 1,910 (+1719.05%)
Mutual labels:  fp
nef-editor-client
๐Ÿ“ฑClient-side code for the nef editor app
Stars: โœญ 20 (-80.95%)
Mutual labels:  fp
Functional Programming Jargon
Jargon from the functional programming world in simple terms!
Stars: โœญ 14,351 (+13567.62%)
Mutual labels:  fp
ramda.py
Python clone of Ramda.js
Stars: โœญ 64 (-39.05%)
Mutual labels:  fp
Bow Arch
๐Ÿ› Functional Architecture in Swift using Bow
Stars: โœญ 166 (+58.1%)
Mutual labels:  fp
React On Lambda
A JavaScript library for building React applications in more functional way. Alternative to JSX.
Stars: โœญ 192 (+82.86%)
Mutual labels:  fp
fp-units
An FP-oriented library to easily convert CSS units.
Stars: โœญ 18 (-82.86%)
Mutual labels:  fp
Metalang99
A functional language for C99 preprocessor metaprogramming
Stars: โœญ 152 (+44.76%)
Mutual labels:  fp
ramdu
Small utils set built around Ramda
Stars: โœญ 18 (-82.86%)
Mutual labels:  fp
Scala Graal
Make usage of Graal features easy and safe from Scala. Also features Scala-based React SSR.
Stars: โœญ 152 (+44.76%)
Mutual labels:  fp
monadic-mondays
Code samples for #monadicmonday topics
Stars: โœญ 86 (-18.1%)
Mutual labels:  fp
tutorials
๐ŸŽฅ Source code of the examples shown in the video tutorials
Stars: โœญ 18 (-82.86%)
Mutual labels:  fp
function-composition-cheatsheet
Composition of Functions
Stars: โœญ 24 (-77.14%)
Mutual labels:  fp
fnts
ฮป Minimal Functional Programming Utilities for TypeScript & JavaScript
Stars: โœญ 75 (-28.57%)
Mutual labels:  fp

Functional Programming Jargon

FP (Functional Programming)๋Š” ๋งŽ์€ ์ด์ ์„ ์ œ๊ณตํ•˜๋ฉฐ ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ธ๊ธฐ๊ฐ€ ๋†’์•„์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์—๋Š” ๊ณ ์œ ํ•œ ํŠน์ˆ˜ ์šฉ์–ด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉฐ FP๋„ ์˜ˆ์™ธ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ์šฉ์–ด์ง‘์„ ์ œ๊ณตํ•จ์œผ๋กœ์จ FP ํ•™์Šต์ด ์‰ฌ์›Œ์ง€๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์˜ˆ์ œ๋Š” JavaScript (ES2015)๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. Why JavaScript?

์ด๊ฒƒ์€ WIP์ž…๋‹ˆ๋‹ค; PR์„ ๋ณด๋‚ด ์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค;)

ํ•ด๋‹น๋˜๋Š” ๊ฒฝ์šฐ, ์ด ๋ฌธ์„œ๋Š” Fantasy Land spec์— ์ •์˜๋œ ์šฉ์–ด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Table of Contents

Arity

ํ•จ์ˆ˜๊ฐ€ ์ทจํ•˜๋Š” ์ธ์ˆ˜์˜ ๊ฐฏ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋‹จํ•ญ, ์ด์›, ์‚ผํ•ญ ๋“ฑ์˜ ๋‹จ์–ด์—์„œ ์ด ๋‹จ์–ด๋Š” ๋‘ ๊ฐœ์˜ ์ ‘๋ฏธ์‚ฌ "--ary"์™€ "--ity"๋กœ ๊ตฌ๋ถ„๋œ๋‹ค๋Š” ๊ตฌ๋ณ„์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋”ํ•˜๊ธฐ๋Š” ๋‘ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ์ทจํ•˜๋ฏ€๋กœ ์ด์ง„ ํ•จ์ˆ˜ ๋˜๋Š” 2์˜ arity๋ฅผ โ€‹โ€‹๊ฐ–๋Š” ํ•จ์ˆ˜๋กœ ์ •์˜๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌํ•œ ํ•จ์ˆ˜๋Š” ๋ผํ‹ด์–ด์—์„œ ๊ทธ๋ฆฌ์Šค์–ด๋กœ ๋ฟŒ๋ฆฌ๋ฅผ ์„ ํ˜ธํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์— ์˜ํ•ด ๋•Œ๋•Œ๋กœ "dyadic"์ด๋ผ๊ณ  ๋ถˆ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋น„์Šทํ•˜๊ฒŒ ๋ณ€์ˆ˜ ๊ฐœ์ˆ˜์˜ ์ธ์ˆ˜๋ฅผ ์ทจํ•˜๋Š” ํ•จ์ˆ˜๋Š” "variadic"์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ๋ฐ˜๋ฉด, 2์ง„ ํ•จ์ˆ˜๋Š” currying๊ณผ ๋ถ€๋ถ„ ์ ์šฉ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๋‘ ๊ฐœ์˜ ์ธ์ˆ˜๋งŒ ์ œ๊ณตํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค (์•„๋ž˜ ์ฐธ์กฐ).

const sum = (a, b) => a + b

const arity = sum.length
console.log(arity) // 2

// The arity of sum is 2

Higher-Order Functions (HOF)

ํ•จ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

const filter = (predicate, xs) => xs.filter(predicate)
const is = (type) => (x) => Object(x) instanceof type
filter(is(Number), [0, '1', 2, null]) // [0, 2]

Partial Application

ํ•จ์ˆ˜๋ฅผ ๋ถ€๋ถ„์ ์œผ๋กœ ์ ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ(Partial Application)์€ ์ธ์ˆ˜ ์ค‘ ์ผ๋ถ€๋ฅผ ์›๋ž˜ ํ•จ์ˆ˜์— ๋ฏธ๋ฆฌ ์ฑ„์›Œ์„œ ์ƒˆ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

// ๋ถ€๋ถ„์ ์œผ๋กœ ์ ์šฉ๋œ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“œ๋Š” ๋„์šฐ๋ฏธ
// ํ•จ์ˆ˜์™€ ๋ช‡ ๊ฐ€์ง€ ์ธ์ˆ˜๋ฅผ ์ทจํ•ฉ๋‹ˆ๋‹ค.
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

Function.prototype.bind๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ถ€๋ถ„์ ์œผ๋กœ ์ ์šฉํ• ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค :

const add1More = add3.bind(null, 2, 3) // (c) => 2 + 3 + c

Partial application์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์œ  ํ•  ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ž‘ํ•ด์„œ ๋ณด๋‹ค ๊ฐ„๋‹จํ•œ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Curried ํ•จ์ˆ˜๋Š” ๋ถ€๋ถ„์ ์œผ๋กœ ์ž๋™ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

Currying

์—ฌ๋Ÿฌ ์ธ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์”ฉ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ”„๋กœ์„ธ์Šค์ž…๋‹ˆ๋‹ค.

ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ํ•˜๋‚˜์˜ ์ธ์ˆ˜๋งŒ ํ—ˆ์šฉํ•˜๊ณ  ๋ชจ๋“  ์ธ์ˆ˜๊ฐ€ ์ „๋‹ฌ๋  ๋•Œ๊นŒ์ง€ ์ธ์ˆ˜ ํ•˜๋‚˜๋ฅผ ์ทจํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

const 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

Closure

ํด๋กœ์ €(Closure)๋Š” ๋ฒ”์œ„ ์™ธ๋ถ€์˜ ๋ณ€์ˆ˜์— ์•ก์„ธ์Šคํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๊ณต์‹์ ์œผ๋กœ ํด๋กœ์ €๋Š” ์ •์  ์Šค์ฝ”ํ”„๊ฐ€ ์ง€์ •๋œ ๋ฐ”์ธ๋”ฉ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ™˜๊ฒฝ๊ณผ ์‹คํ–‰์ฝ”๋“œ(ํ•จ์ˆ˜)๋ฅผ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค.

ํด๋กœ์ €๋Š” ์‹คํ–‰์ด ์ •์˜๋œ ๋ธ”๋ก ๋ฐ–์œผ๋กœ ์ด๋™ํ•œ ํ›„์—๋„ ํ•จ์ˆ˜์˜ ๋กœ์ปฌ๋ณ€์ˆ˜๋ฅผ ์•ก์„ธ์Šค์šฉ์œผ๋กœ ์บก์ฒ˜ํ•˜๋Š” ๋ฒ”์œ„์ž…๋‹ˆ๋‹ค. ์ฆ‰. ๋ณ€์ˆ˜๊ฐ€ ์„ ์–ธ๋œ ๋ธ”๋ก์ด ์‹คํ–‰์„ ๋งˆ์นœ ํ›„์— ๋ฒ”์œ„๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const addTo = x => y => x + y
var addToFive = addTo(5)
addToFive(3) // returns 8

addTo() ํ•จ์ˆ˜๋Š” (๋‚ด๋ถ€์ ์œผ๋กœ add ()๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š”) ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ ๋งค๊ฐœ ๋ณ€์ˆ˜ 5๋ฅผ ๊ฐ–๋Š” curry ํ˜ธ์ถœ๋กœ addToFive๋ผ๋Š” ๋ณ€์ˆ˜์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ด์ƒ์ ์œผ๋กœ addTo ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰์„ ๋๋‚ด๋ฉด ๊ทธ ๋ฒ”์œ„๋Š” ์ง€์—ญ ๋ณ€์ˆ˜ add, x, y๋กœ ์ ‘๊ทผ ํ•  ์ˆ˜ ์—†์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ addToFive()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด 8์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ ๋ธ”๋ก์ด ์‹คํ–‰์„ ๋งˆ์นœ ํ›„์—๋„ addTo ํ•จ์ˆ˜์˜ ์ƒํƒœ๊ฐ€ ์ €์žฅ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด addTo๊ฐ€ addTo(5)๋กœ ํ˜ธ์ถœ๋˜๊ณ  x ๊ฐ’์ด 5๋กœ ์„ค์ •๋˜์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค.

์–ดํœ˜์  ์œ ํšจ ๋ฒ”์œ„๋Š” x ๋ฐ add ๊ฐ’ (์‹คํ–‰์„ ์™„๋ฃŒํ•œ ๋ถ€๋ชจ์˜ ๊ฐœ์ธ ๋ณ€์ˆ˜)์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. ์ด ๊ฐ’์„ Closure๋ผ๊ณ ํ•ฉ๋‹ˆ๋‹ค.

์Šคํƒ์€ ํ•จ์ˆ˜์˜ ์–ดํœ˜์  ์œ ํšจ ๋ฒ”์œ„์™€ ํ•จ๊ป˜ ๋ถ€๋ชจ์— ๋Œ€ํ•œ ์ฐธ์กฐ ํ˜•์‹์œผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒํ•˜๋ฉด ํด๋กœ์ €์™€ ๊ธฐ๋ณธ๋ณ€์ˆ˜๊ฐ€ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.(์ ์–ด๋„ ํ•˜๋‚˜์˜ ์‹ค์ œ ์ฐธ์กฐ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ)

Lambda Vs Closure :

Lambda๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•˜๋Š” ํ‘œ์ค€ ๋ฐฉ๋ฒ•์ด ์•„๋‹Œ ์ธ๋ผ์ธ์œผ๋กœ ์ •์˜๋œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. Lambda๋Š” ์ข…์ข… ๊ฐ์ฒด๋กœ ์ „๋‹ฌ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Closure๋Š” ๋ชธ์˜ ์™ธ๋ถ€ ํ•„๋“œ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ์ฃผ๋ณ€์ƒํƒœ๋ฅผ ๋‘˜๋Ÿฌ์‹ผ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋‹ซํžŒ์ƒํƒœ๋Š” ํด๋กœ์ €์˜ ํ˜ธ์ถœ์„ ๊ฐ€๋กœ์งˆ๋Ÿฌ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

์ถ”๊ฐ€ ์ฝ๊ธฐ / ์ถœ์ฒ˜

Auto Currying

์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ์ทจํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ•˜๋‚˜์˜ ์ธ์ˆ˜๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด ์˜ฌ๋ฐ”๋ฅธ ์ˆ˜์˜ ์ธ์ˆ˜๋ณด๋‹ค ์ž‘์œผ๋ฉด ๋‚˜๋จธ์ง€๋ฅผ ์ทจํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. ํ•จ์ˆ˜๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ์ˆ˜์˜ ์ธ์ˆ˜๋ฅผ ์–ป์œผ๋ฉด ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค.

lodash & Ramda๋Š” ์ด๋Ÿฐ์‹์œผ๋กœ ์ž‘๋™ํ•˜๋Š” curryํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

const add = (x, y) => x + y

const curriedAdd = _.curry(add)
curriedAdd(1, 2) // 3
curriedAdd(1) // (y) => 1 + y
curriedAdd(1)(2) // 3

์ถ”๊ฐ€ ์ฝ๊ธฐ

Function Composition

ํ•˜๋‚˜์˜ ํ•จ์ˆ˜ ์ถœ๋ ฅ์ด ๋‹ค๋ฅธ ํ•จ์ˆ˜์˜ ์ž…๋ ฅ์ธ ์ œ 3์˜ ํ•จ์ˆ˜๋ฅผ ํ˜•์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๋‘ ํ•จ์ˆ˜๋ฅผ ํ•จ๊ป˜ ๋ชจ์œผ๋Š” ํ–‰์œ„์ž…๋‹ˆ๋‹ค.

const compose = (f, g) => (a) => f(g(a)) // ์ •์˜
const floorAndToString = compose((val) => val.toString(), Math.floor) // ์‚ฌ์šฉ๋ฒ•
floorAndToString(121.212121) // '121'

Continuation

ํ”„๋กœ๊ทธ๋žจ์˜ ํŠน์ • ์ง€์ ์—์„œ ์ฝ”๋“œ์˜ ์‹คํ–‰๋˜์ง€ ์•Š์€ ๋ถ€๋ถ„์„ ๊ณ„์†(Continuation)์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

const printAsString = (num) => console.log(`Given ${num}`)

const addOneAndContinue = (num, cc) => {
  const result = num + 1
  cc(result)
}

addOneAndContinue(2, printAsString) // 'Given 3'

๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ๊ณ„์† ์ง„ํ–‰ํ•˜๊ธฐ ์ „์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ธฐ ์œ„ํ•ด ๋Œ€๊ธฐํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ข…์ข… ์žˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต์€ ํ”„๋กœ๊ทธ๋žจ์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์œผ๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ข…์ข… ์žˆ์Šต๋‹ˆ๋‹ค.

const continueProgramWith = (data) => {
  // ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ ํ”„๋กœ๊ทธ๋žจ์„ ๊ณ„์†ํ•œ๋‹ค.
}

readFileAsync('path/to/file', (err, response) => {
  if (err) {
    // handle error
    return
  }
  continueProgramWith(response)
})

Purity

๋ฐ˜ํ™˜ ๊ฐ’์ด ์ž…๋ ฅ ๊ฐ’์— ์˜ํ•ด์„œ๋งŒ ๊ฒฐ์ •๋˜๊ณ  ๋ถ€์ˆ˜ํšจ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฉด ํ•จ์ˆ˜๋Š” ์ˆœ์ˆ˜(Purity)ํ•ฉ๋‹ˆ๋‹ค.

const greet = (name) => `Hi, ${name}`

greet('Brianne') // 'Hi, Brianne'

๋‹ค์Œ๊ณผ ๊ฐ™์ด:

window.name = 'Brianne'

const greet = () => `Hi, ${window.name}`

greet() // "Hi, Brianne"

์œ„ ์˜ˆ์ œ์˜ ์ถœ๋ ฅ์€ ํ•จ์ˆ˜ ์™ธ๋ถ€์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค...

let greeting

const greet = (name) => {
  greeting = `Hi, ${name}`
}

greet('Brianne')
greeting // "Hi, Brianne"

... ๊ทธ๋ฆฌ๊ณ  ์ด ํ•จ์ˆ˜ ๋ฐ–์—์„œ ์ƒํƒœ๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

Side effects

ํ•จ์ˆ˜ ๋˜๋Š” ํ‘œํ˜„์‹์€ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ๊ณผ๋Š” ๋ณ„๋„๋กœ ์™ธ๋ถ€ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ ์ƒํƒœ์™€ ์ƒํ˜ธ ์ž‘์šฉ (์ฝ๊ธฐ ๋˜๋Š” ์“ฐ๊ธฐ)ํ•˜๋Š” ๊ฒฝ์šฐ ๋ถ€์ˆ˜ํšจ๊ณผ(Side effects)๊ฐ€ ์žˆ๋‹ค๊ณ ํ•ฉ๋‹ˆ๋‹ค.

const differentEveryTime = new Date()
console.log('IO is a side effect!')

Idempotent

ํ•จ์ˆ˜์— ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ์ ์šฉํ•ด๋„ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š์œผ๋ฉด ํ•จ์ˆ˜๋Š” ๋ฉฑ๋“ฑ์„ฑ(Idempotent)์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

f(f(x)) โ‰ f(x)
Math.abs(Math.abs(10))
sort(sort(sort([2, 1])))

Point-Free Style

์ •์˜๊ฐ€ ์‚ฌ์šฉ๋œ ์ธ์ˆ˜๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์‹๋ณ„ํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜ ์ž‘์„ฑ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์ด ์Šคํƒ€์ผ์€ ์ผ๋ฐ˜์ ์œผ๋กœ currying ๋˜๋Š” ๋‹ค๋ฅธ Higher-Order functions์„ ํ•„์š”๋กœํ•ฉ๋‹ˆ๋‹ค. A.K.A Tacit programming.

// 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))

incrementAll์€ ๋งค๊ฐœ ๋ณ€์ˆ˜ numbers๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹๋ณ„ํ•˜๋ฏ€๋กœ Point-free ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. incrementAll2๋Š” ํ•จ์ˆ˜์™€ ๊ฐ’์„ ๊ฒฐํ•ฉํ•˜์—ฌ ์ž‘์„ฑ๋˜๋ฉฐ ์ธ์ˆ˜์— ๋Œ€ํ•ด์„œ๋Š” ์–ธ๊ธ‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Point-freeํ•ฉ๋‹ˆ๋‹ค.

points-free ํ•จ์ˆ˜ ์ •์˜๋Š” function ๋˜๋Š” => ์—†์ด ์ผ๋ฐ˜์ ์ธ ํ• ๋‹น์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค.

Predicate

์ˆ ๋ถ€(Predicate)๋Š” ์ฃผ์–ด์ง„ ๊ฐ’์— ๋Œ€ํ•ด ์ฐธ ๋˜๋Š” ๊ฑฐ์ง“์„ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ˆ ๋ถ€(Predicate)์˜ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ์€ ๋ฐฐ์—ด ํ•„ํ„ฐ์˜ ์ฝœ๋ฐฑ์ž…๋‹ˆ๋‹ค.

const predicate = (a) => a > 2

;[1, 2, 3, 4].filter(predicate) // [3, 4]

Contracts

๊ณ„์•ฝ(Contracts)์€ ๋Ÿฐํƒ€์ž„์— ํ•จ์ˆ˜ ๋˜๋Š” ํ‘œํ˜„์‹์œผ๋กœ๋ถ€ํ„ฐ ํ–‰์œ„์˜ ์˜๋ฌด ๋ฐ ๋ณด์ฆ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ•จ์ˆ˜ ๋˜๋Š” ํ‘œํ˜„์‹์˜ ์ž…๋ ฅ ๋ฐ ์ถœ๋ ฅ์—์„œ ์˜ˆ์ƒ๋˜๋Š” ์ผ๋ จ์˜ ๊ทœ์น™์œผ๋กœ ์ž‘๋™ํ•˜๋ฉฐ ์ผ๋ฐ˜์ ์œผ๋กœ ๊ณ„์•ฝ ์œ„๋ฐ˜์‹œ ์˜ค๋ฅ˜๊ฐ€ ๋ณด๊ณ ๋ฉ๋‹ˆ๋‹ค.

// Define our contract : int -> int
const contract = (input) => {
  if (typeof input === 'number') return true
  throw new Error('Contract violated: expected int -> int')
}

const addOne = (num) => contract(num) && num + 1

addOne(2) // 3
addOne('some string') // Contract violated: expected int -> int

Category

๋ฒ”์ฃผ ์ด๋ก ์˜ category๋Š” ๊ฐ์ฒด์™€ ๊ฐ์ฒด๊ฐ„์˜ morphism ๋ชจ์Œ์ง‘์ž…๋‹ˆ๋‹ค. ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ์‚ฌ์šฉํ•˜๋Š” category๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ํƒ€์ž…์„ ๊ฐ์ฒด๋กœ, ํ•จ์ˆ˜๋ฅผ ๊ฐ์ฒด ์‚ฌ์ด์˜ morphism ์œผ๋กœ ๊ฐ€์ง€๋Š” category์ž…๋‹ˆ๋‹ค.

์œ ํšจํ•œ Category๊ฐ€ ๋˜๊ธฐ ์œ„ํ•ด์„œ๋Š” 3 ๊ฐ€์ง€ ๊ทœ์น™์ด ์ถฉ์กฑ๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค:

  1. ๊ฐ์ฒด๋ฅผ ๊ทธ ์ž์ฒด๋กœ ๋งคํ•‘ํ•˜๋Š” identity morphism์ด ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. a๊ฐ€ ์–ด๋–ค Category์˜ ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ, a -> a์˜ ํ•จ์ˆ˜๊ฐ€ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.
  2. morphism์ด ํ•ฉ์„ฑ๋˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ a, b ๋ฐ c๋Š” ์–ด๋–ค ๋ฒ”์ฃผ์˜ ๊ฐ์ฒด์ด๊ณ , f๋Š” a -> b์˜ morphism์ด๊ณ  g๋Š” b -> c์˜ morphism์ž…๋‹ˆ๋‹ค; (g โ€ข f)(x)๋Š” g(f(x))์™€ ๊ฐ™์•„์•ผํ•ฉ๋‹ˆ๋‹ค.
  3. ํ•ฉ์„ฑ์€ ์—ฐ๊ด€์„ฑ์ด ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. f โ€ข (g โ€ข h)๋Š” (f โ€ข g) โ€ข h์™€ ๊ฐ™์•„์•ผํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ทœ์น™์€ ๋งค์šฐ ์ถ”์ƒ์ ์ธ ์ˆ˜์ค€์—์„œ ํ•ฉ์„ฑ์„ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— Category ์ด๋ก ์€ ๊ฐ์ฒด๋ฅผ ํ•ฉ์„ฑํ•˜๋Š” ์ƒˆ๋กœ์šด ๋ฐฉ๋ฒ•์„ ๋ฐœ๊ฒฌํ•˜๋Š”๋ฐ ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

์ถ”๊ฐ€ ์ฝ๊ธฐ

Value

๋ณ€์ˆ˜์— ํ• ๋‹น ํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

5
Object.freeze({name: 'John', age: 30}) // `freeze` ํ•จ์ˆ˜๋Š” ๋ถˆ๋ณ€์„ฑ์„ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค.
;(a) => a
;[1]
undefined

Constant

ํ•œ ๋ฒˆ ํ• ๋‹นํ•˜๋ฉด ๋‹ค์‹œ ํ• ๋‹นํ•  ์ˆ˜ ์—†๋Š” ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค.

const five = 5
const john = Object.freeze({name: 'John', age: 30})

์ƒ์ˆ˜๋Š” ์ฐธ์กฐ ํˆฌ๋ช…์„ฑ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ์ฆ‰, ๊ฒฐ๊ณผ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๊ณ  ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„์˜ ๋‘ ์ƒ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์Œ ํ‘œํ˜„์‹์ด ํ•ญ์ƒ true๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

john.age + five === ({name: 'John', age: 30}).age + (5)

Functor

๊ฐ์ฒด์˜ ๊ฐ ๊ฐ’์„ ์‹คํ–‰ํ•˜๋ฉด์„œ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋™์•ˆ map ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฐ์ฒด๋Š” ๋‘ ๊ฐ€์ง€ ๊ทœ์น™์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค :

์‹ ์› ๋ณด์กด

object.map(x => x) โ‰ object

ํ•ฉ์„ฑ ๊ฐ€๋Šฅ

object.map(compose(f, g)) โ‰ object.map(g).map(f)

(์ž„์˜์˜ ํ•จ์ˆ˜ f, g)

์ž๋ฐ” ์Šคํฌ๋ฆฝํŠธ์˜ Functor์ธ Array๋Š” ๋‘ ๊ฐ€์ง€ Functor ๊ทœ์น™์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค:

;[1, 2, 3].map(x => x) // = [1, 2, 3]

๊ทธ๋ฆฌ๊ณ 

const f = x => x + 1
const g = x => x * 2

;[1, 2, 3].map(x => f(g(x))) // = [3, 5, 7]
;[1, 2, 3].map(g).map(f)     // = [3, 5, 7]

Pointed Functor

์ž„์˜ ์˜ ๋‹จ์ผ ๊ฐ’์„ ๋„ฃ๋Š” of ํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค .

ES2015์—์„œ Array๋ฅผ pointed functor๋กœ ๋งŒ๋“œ๋Š” Array.of๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

Array.of(1) // [1]

Lift

Lifting์€ ๊ฐ’์„ ๊ฐ€์ ธ ์™€์„œ functor์™€ ๊ฐ™์€ ๊ฐ์ฒด์— ๋„ฃ๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜๋ฅผ Applicative Functor๋กœ ๋“ค์–ด ์˜ฌ๋ฆฌ๋ฉด ํ•ด๋‹น Functor์—๋„ ์žˆ๋Š” ๊ฐ’์— ๋Œ€ํ•ด ํ•จ์ˆ˜๋ฅผ ์ ์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ๋ถ€ ๊ตฌํ˜„์—์„œ๋Š” lift, liftA2๋ผ ๋ถˆ๋ฆฌ๋Š” ํ•จ์ˆ˜๊ฐ€ functor์—์„œ ํ•จ์ˆ˜๋ฅผ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ์‹คํ–‰ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

const liftA2 = (f) => (a, b) => a.map(f).ap(b) // ์ด๊ฒƒ์€ `ap`์ด์ง€,`map`์ด ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์— ์ฃผ์˜ํ•˜์‹ญ์‹œ์˜ค.

const mult = a => b => a * b

const liftedMult = liftA2(mult) // ์ด ํ•จ์ˆ˜๋Š” ์ด์ œ ๋ฐฐ์—ด๊ณผ ๊ฐ™์€ functor์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

liftedMult([1, 2], [3]) // [3, 6]
liftA2(a => b => a + b)([1, 2], [3, 4]) // [4, 5, 5, 6]

map๊ณผ ๊ฐ™์ด ํ•˜๋‚˜์˜ ์ธ์ž๋ฅผ ๊ฐ–๋Š” ํ•จ์ˆ˜๋ฅผ ๋“ค์–ด ์˜ฌ๋ฆฌ๊ณ  ์ ์šฉํ•˜๋Š” ์ผ์„ ํ•ฉ๋‹ˆ๋‹ค.

const increment = (x) => x + 1

lift(increment)([2]) // [3]
;[2].map(increment) // [3]

Referential Transparency

ํ”„๋กœ๊ทธ๋žจ์˜ ๋™์ž‘์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ๊ฐ’์œผ๋กœ ๋Œ€์ฒด ํ•  ์ˆ˜ ์žˆ๋Š” ํ‘œํ˜„์‹์€ ์ฐธ์กฐํˆฌ๋ช…์„ฑ(Referential Transparency)์„ ๊ฐ€์ง„๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ greet ํ•จ์ˆ˜๋ฅผ ๊ฐ€์กŒ๋‹ค๊ณ  ํ•˜๋ฉด :

const greet = () => 'Hello World!'

Hello World!๋ฅผ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋Š” greet()๋Š” ์ฐธ์กฐํˆฌ๋ช…์„ฑ(Referential Transparency)์ž…๋‹ˆ๋‹ค.

Equational Reasoning

์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ํ‘œํ˜„์‹์œผ๋กœ ํ•ฉ์„ฑ๋˜์–ด ๋ถ€์ˆ˜ํšจ๊ณผ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ์ง„์‹ค์€ ํ•ด๋‹น ๋ถ€๋ถ„์—์„œ ํŒŒ์ƒ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Lambda

๊ฐ’์ฒ˜๋Ÿผ ์ทจ๊ธ‰ ๋  ์ˆ˜ ์žˆ๋Š” ์ต๋ช…์˜ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

;(function (a) {
  return a + 1
})

;(a) => a + 1

๋žŒ๋‹ค๋Š” ์ข…์ข… ๊ณ ์ฐจ ํ•จ์ˆ˜์— ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

;[1, 2].map((a) => a + 1) // [2, 3]

๋ณ€์ˆ˜์— ๋žŒ๋‹ค๋ฅผ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const add1 = (a) => a + 1

Lambda Calculus

ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ universal model of computation์„ ๋งŒ๋“œ๋Š” ์ˆ˜ํ•™ ๋ถ„์•ผ์ž…๋‹ˆ๋‹ค.

Lazy evaluation

๊ฒŒ์œผ๋ฅธ ํ‰๊ฐ€(Lazy evaluation)๋Š” ๊ฐ’์ด ํ•„์š”ํ•  ๋•Œ๊นŒ์ง€ ํ‘œํ˜„์‹ ํ‰๊ฐ€๋ฅผ ์ง€์—ฐ์‹œํ‚ค๋Š” ํ˜ธ์ถœ ๋ณ„ ํ‰๊ฐ€ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜ํ˜• ์–ธ์–ด์—์„œ๋Š” ๋ฌดํ•œํ•œ ๋ชฉ๋ก๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๊ฐ€ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด ๊ตฌ์กฐ๋Š” ๋ช…๋ น ์ˆœ์„œ ์ง€์ •์ด ์ค‘์š”ํ•œ ๋ช…๋ นํ˜• ์–ธ์–ด์—์„œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

const rand = function*() {
  while (1 < 2) {
    yield Math.random()
  }
}
const randIter = rand()
randIter.next() // ๊ฐ ์‹คํ–‰๋งˆ๋‹ค ์ž„์˜์˜ ๊ฐ’์ด ์ฃผ์–ด์ง€๋ฉฐ ํ•„์š”์— ๋”ฐ๋ผ ํ‘œํ˜„์‹์ด ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค.

Monoid

๊ฐ์ฒด๋ฅผ ๊ฐ™์€ ํƒ€์ž…์˜ ๋‹ค๋ฅธ ๊ฐ์ฒด์™€ "๊ฒฐํ•ฉ"ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

ํ•˜๋‚˜์˜ ๊ฐ„๋‹จํ•œ monoid๋Š” ์ˆซ์ž ์ถ”๊ฐ€์ž…๋‹ˆ๋‹ค :

1 + 1 // 2

์ด ๊ฒฝ์šฐ number๋Š” ๊ฐ์ฒด์ด๋ฉฐ +๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

๊ฐ’๊ณผ ๊ฒฐํ•ฉ ๋  ๋•Œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š” "ID"๊ฐ’์ด ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

์ถ”๊ฐ€ ํ•  ID ๊ฐ’์€ 0์ž…๋‹ˆ๋‹ค.

1 + 0 // 1

๋˜ํ•œ ์ž‘์—…์˜ ๊ทธ๋ฃนํ™”๊ฐ€ ๊ฒฐ๊ณผ (์—ฐ๊ด€์„ฑ)์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์•„์•ผํ•ฉ๋‹ˆ๋‹ค :

1 + (2 + 3) === (1 + 2) + 3 // true

๋ฐฐ์—ด ์—ฐ๊ฒฐ์€ ๋˜ํ•œ monoid๋ฅผ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค :

;[1, 2].concat([3, 4]) // [1, 2, 3, 4]

์•„์ด๋”” ๊ฐ’์€ ๋นˆ ๋ฐฐ์—ด []์ž…๋‹ˆ๋‹ค :

;[1, 2].concat([]) // [1, 2]

identity์™€ compose ํ•จ์ˆ˜๊ฐ€ ์ œ๊ณต๋˜๋ฉด, ํ•จ์ˆ˜ ์ž์ฒด๋Š” monoid๋ฅผ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค :

const identity = (a) => a
const compose = (f, g) => (x) => f(g(x))

foo๋Š” ํ•˜๋‚˜์˜ ์ธ์ˆ˜๋ฅผ ์ทจํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

compose(foo, identity) โ‰ compose(identity, foo) โ‰ foo

Monad

Monad๋Š” ์–ด๋–ค ํƒ€์ž…๊ณผ ๊ทธ ํƒ€์ž…์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” of ์™€ chain ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. chain์€ ์ค‘์ฒฉ๋œ ๊ฒฐ๊ณผ๋ฅผ ์ค‘์ฒฉ๋˜์ง€ ์•Š๊ฒŒ ํ•˜๋Š”๊ฒƒ์„ ์ œ์™ธํ•˜๊ณ  map์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.

// ๊ตฌํ˜„
Array.prototype.chain = function (f) {
  return this.reduce((acc, it) => acc.concat(f(it)), [])
}

// ์‚ฌ์šฉ๋ฒ•
Array.of('cat,dog', 'fish,bird').chain((a) => a.split(',')) // ['cat', 'dog', 'fish', 'bird']

// map๊ณผ ๋น„๊ต
Array.of('cat,dog', 'fish,bird').map((a) => a.split(',')) // [['cat', 'dog'], ['fish', 'bird']]

of๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜ํ˜• ์–ธ์–ด์—์„œ return์ด๋ผ๊ณ ๋„ ํ•ฉ๋‹ˆ๋‹ค.

chain์€ ๋‹ค๋ฅธ ์–ธ์–ด์—์„œflatmap ๊ณผ bind๋ผ๊ณ ๋„ ํ•ฉ๋‹ˆ๋‹ค.

Comonad

extract ์™€ extend ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

const CoIdentity = (v) => ({
  val: v,
  extract () {
    return this.val
  },
  extend (f) {
    return CoIdentity(f(this))
  }
})

Extract์€ functor์—์„œ ๊ฐ’์„ ์ทจํ•ฉ๋‹ˆ๋‹ค.

CoIdentity(1).extract() // 1

Extend๋Š” comonad์—์„œ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” comonad์™€ ๋™์ผํ•œ ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

CoIdentity(1).extend((co) => co.extract() + 1) // CoIdentity(2)

Applicative Functor

Applicative Functor๋Š” ap ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ap์€ ๊ฐ์ฒด์˜ ํ•จ์ˆ˜๋ฅผ ๊ฐ™์€ ํƒ€์ž…์˜ ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ๊ฐ’์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

// ๊ตฌํ˜„ 
Array.prototype.ap = function (xs) {
  return this.reduce((acc, f) => acc.concat(xs.map(f)), [])
}

// ์˜ˆ์ œ ์‚ฌ์šฉ๋ฒ•
;[(a) => a + 1].ap([1]) // [2]

์ด๋Š” ๋‘ ๊ฐœ์˜ ๊ฐ์ฒด๊ฐ€ ์žˆ๊ณ  ๊ทธ ๋‚ด์šฉ์— ์ด์ง„ ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•˜๋ ค๋Š” ๊ฒฝ์šฐ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

// ๊ฒฐํ•ฉํ•˜๋ ค๋Š” ๋ฐฐ์—ด 
const arg1 = [1, 3]
const arg2 = [4, 5]

//๊ฒฐํ•ฉ ๊ธฐ๋Šฅ - ์ด๊ฒƒ์ด ์ž‘๋™ํ•˜๋ ค๋ฉด curryํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
const add = (x) => (y) => x + y

const partiallyAppliedAdds = [add].ap(arg1) // [(y) => 1 + y, (y) => 3 + y]

์ด๊ฒƒ์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ap ๋ผ๊ณ  ๋ถ€๋ฅผ ์ˆ˜์žˆ๋Š” ๋ฐฐ์—ด์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค :

partiallyAppliedAdds.ap(arg2) // [5, 6, 7, 8]

Morphism

๋ณ€ํ™˜ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

Endomorphism

์ž…๋ ฅ ํƒ€์ž…์ด ์ถœ๋ ฅ๊ณผ ๋™์ผํ•œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

// uppercase :: String -> String
const uppercase = (str) => str.toUpperCase()

// decrement :: Number -> Number
const decrement = (x) => x - 1

Isomorphism

๊ตฌ์กฐ์ ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ์†์‹ค๋˜์ง€ ์•Š๋Š” ๋‘ ๊ฐ€์ง€ ํƒ€์ž… ๊ฐ์ฒด๊ฐ„์˜ ๋ณ€ํ™˜์Œ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, 2D ์ขŒํ‘œ๋Š” ๋ฐฐ์—ด [2,3]๋˜๋Š” ๊ฐ์ฒด {x: 2, y: 3}๋กœ ์ €์žฅ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ์–‘๋ฐฉํ–ฅ์œผ๋กœ ๋ณ€ํ™˜ ํ•  ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜๋ฉด isomorphic์ด ๋ฉ๋‹ˆ๋‹ค.
const pairToCoords = (pair) => ({x: pair[0], y: pair[1]})

const coordsToPair = (coords) => [coords.x, coords.y]

coordsToPair(pairToCoords([1, 2])) // [1, 2]

pairToCoords(coordsToPair({x: 1, y: 2})) // {x: 1, y: 2}

Setoid

๋™์ผํ•œ ํƒ€์ž…์˜ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๋น„๊ตํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” equalsํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

๋ฐฐ์—ด์„ Setoid๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค:

Array.prototype.equals = function (arr) {
  const len = this.length
  if (len !== arr.length) {
    return false
  }
  for (let i = 0; i < len; i++) {
    if (this[i] !== arr[i]) {
      return false
    }
  }
  return true
}

;[1, 2].equals([1, 2]) // true
;[1, 2].equals([0]) // false

Semigroup

๋™์ผํ•œ ํƒ€์ž…์˜ ๋‹ค๋ฅธ ๊ฐ์ฒด์™€ ๊ฒฐํ•ฉํ•˜๋Š” concat ํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

;[1].concat([2]) // [1, 2]

Foldable

๊ฐ์ฒด๋ฅผ ๋‹ค๋ฅธ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ ํ•  ์ˆ˜์žˆ๋Š” reduce ํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

const sum = (list) => list.reduce((acc, val) => acc + val, 0)
sum([1, 2, 3]) // 6

Lens

Lens๋Š” ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์œ„ํ•ด getter์™€ non-mutating setter๋ฅผ ์Œ์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ตฌ์กฐ์ฒด (์ข…์ข… ๊ฐ์ฒด ๋˜๋Š” ํ•จ์ˆ˜)์ž…๋‹ˆ๋‹ค.

// Using [Ramda's lens](http://ramdajs.com/docs/#lens)
const nameLens = R.lens(
  //๊ฐ์ฒด์˜ name ์†์„ฑ์— ๋Œ€ํ•œ getter
  (obj) => obj.name,
  // ์ด๋ฆ„ ์†์„ฑ์— ๋Œ€ํ•œ setter
  (val, obj) => Object.assign({}, obj, {name: val})
)

์ฃผ์–ด์ง„ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์— ๋Œ€ํ•ด get๊ณผ set์˜ ์Œ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ช‡ ๊ฐ€์ง€ ์ฃผ์š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const person = {name: 'Gertrude Blanch'}

// getter๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
R.view(nameLens, person) // 'Gertrude Blanch'

// setter๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
R.set(nameLens, 'Shafi Goldwasser', person) // {name: 'Shafi Goldwasser'}

// ๊ตฌ์กฐ์ฒด์˜ ๊ฐ’์— ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
R.over(nameLens, uppercase, person) // {name: 'GERTRUDE BLANCH'}

Lens๋„ ํ•ฉ์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ค‘์ฒฉ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ์ด Lens๋Š” ๋น„์–ด์žˆ์ง€ ์•Š์€ ๋ฐฐ์—ด์˜ ์ฒซ ๋ฒˆ์งธ ํ•ญ๋ชฉ์— ์ดˆ์ ์„ ๋งž์ถฅ๋‹ˆ๋‹ค.
const firstLens = R.lens(
  // ๋ฐฐ์—ด์˜ ์ฒซ ๋ฒˆ์งธ ํ•ญ๋ชฉ ๊ฐ€์ ธ ์˜ค๊ธฐ
  xs => xs[0],
  // ๋ฐฐ์—ด์˜ ์ฒซ ๋ฒˆ์งธ ํ•ญ๋ชฉ์— ๋Œ€ํ•œ ๋น„ ๋ณ€ํ˜• setter
  (val, [__, ...xs]) => [val, ...xs]
)

const people = [{name: 'Gertrude Blanch'}, {name: 'Shafi Goldwasser'}]

// ๋‹น์‹ ์ด ๊ฐ€์ •ํ•  ์ˆ˜๋„ ์žˆ๊ฒ ์ง€๋งŒ, Lens๋Š” ์™ผ์ชฝ์—์„œ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.
R.over(compose(firstLens, nameLens), uppercase, people) // [{'name': 'GERTRUDE BLANCH'}, {'name': 'Shafi Goldwasser'}]

๋‹ค๋ฅธ ๊ตฌํ˜„:

Type Signatures

์ข…์ข… JavaScript์˜ ํ•จ์ˆ˜์—๋Š” ์ธ์ˆ˜์˜ ํƒ€์ž…๊ณผ ๋ฐ˜ํ™˜ ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š” ์ฃผ์„์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

์ปค๋ฎค๋‹ˆํ‹ฐ ์ „๋ฐ˜์— ๊ฑธ์ณ ์•ฝ๊ฐ„์˜ ์ฐจ์ด๊ฐ€ ์žˆ์ง€๋งŒ ๋‹ค์Œ ํŒจํ„ด์„ ์ž์ฃผ ๋”ฐ๋ฅด๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค:

// functionName :: firstArgType -> secondArgType -> returnType

// add :: Number -> Number -> Number
const add = (x) => (y) => x + y

// increment :: Number -> Number
const increment = (x) => x + 1

ํ•จ์ˆ˜๊ฐ€ ์ธ์ˆ˜๋กœ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ๋ฐ›์•„๋“ค์ด๋ฉด ๊ด„ํ˜ธ ์•ˆ์— ์‹ธ์—ฌ ์žˆ์Šต๋‹ˆ๋‹ค.

// call :: (a -> b) -> a -> b
const call = (f) => (x) => f(x)

a,b, c,d๋Š” ์ธ์ˆ˜๊ฐ€ ์–ด๋–ค ํƒ€์ž…์ด๋“  ๋  ์ˆ˜ ์žˆ์Œ์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ๋ฒ„์ „์˜ map์€ ์–ด๋–ค ํƒ€์ž…์˜ ๊ฐ’์„ ๋‹ค๋ฅธ ํƒ€์ž…์ธ b ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ทจํ•ฉ๋‹ˆ๋‹ค.์ด ํƒ€์ž…์€ a ํƒ€์ž…์˜ ๊ฐ’์˜ ๋ฐฐ์—ด์ด๊ณ , b ํƒ€์ž…์˜ ๊ฐ’์˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

// map :: (a -> b) -> [a] -> [b]
const map = (f) => (list) => list.map(f)

์ถ”๊ฐ€ ์ฝ๊ธฐ

Algebraic data type

๋‹ค๋ฅธ ํƒ€์ž…์„ ์กฐํ•ฉํ•˜์—ฌ ๋งŒ๋“  ๋ณตํ•ฉ ํƒ€์ž…์ž…๋‹ˆ๋‹ค. ๋Œ€์ˆ˜ํƒ€์ž…์˜ ๋‘๊ฐ€์ง€ ์ผ๋ฐ˜์ ์ธ ํด๋ž˜์Šค๋Š” sum๊ณผ product์ž…๋‹ˆ๋‹ค.

Sum type

ํ•ฉ๊ณ„ ํƒ€์ž…(Sum type)์€ ๋‘ ํƒ€์ž…์˜ ๊ฒฐํ•ฉ์œผ๋กœ ๋‹ค๋ฅธ ํƒ€์ž…์œผ๋กœ ๊ฒฐํ•ฉ๋ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ ํƒ€์ž…์—์„œ ๊ฐ€๋Šฅํ•œ ๊ฐ’์˜ ์ˆ˜๋Š” ์ž…๋ ฅ ํƒ€์ž…์˜ ํ•ฉ์ด๊ธฐ ๋•Œ๋ฌธ์— sum์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

JavaScript์—๋Š” ์ด์™€ ๊ฐ™์€ ํƒ€์ž…์ด ์—†์ง€๋งŒ Set์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€์žฅ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

// ์—ฌ๊ธฐ์—๋Š” Set๊ฐ€ ์•„๋‹ˆ๋ผ ์ด ๊ฐ’๋งŒ ๊ฐ€์งˆ ์ˆ˜์žˆ๋‹ค๊ณ  ์ƒ์ƒํ•ด๋ณด์„ธ์š”.
const bools = new Set([true, false])
const halfTrue = new Set(['half-true'])

// weakLogic ํƒ€์ž…์—๋Š” bools๊ณผ halfTrue์˜ ๊ฐ’์˜ sum์ด ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
const weakLogicValues = new Set([...bools, ...halfTrue])

ํ•ฉ๊ณ„ ํƒ€์ž…(Sum type)์€ ๋•Œ๋•Œ๋กœ ํ•ฉ์ง‘ํ•ฉ ํƒ€์ž…, ์ฐจ๋ณ„ํ™”๋œ ํ•ฉ์ง‘ํ•ฉ ๋˜๋Š” ํƒœ๊ทธ๊ฐ€ ์ง€์ •๋œ ํ•ฉ์ง‘ํ•ฉ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

JS์—๋Š” ๊ฒฐํ•ฉ ํƒ€์ž…์„ ์ •์˜ํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š”๋ฐ ๋„์›€์ด ๋˜๋Š” couple libraries๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Flow์˜ union types๊ณผ TypeScript์˜ Enums์ด ์ด์™€ ๊ฐ™์€ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

Product type

Product type์€ ํƒ€์ž…์„ ๋” ์ž˜ ์•Œ๊ณ ์žˆ๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ฒฐํ•ฉํ•ฉ๋‹ˆ๋‹ค.

// point :: (Number, Number) -> {x: Number, y: Number}
const point = (x, y) => ({ x, y })

๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ์ด ๊ฐ€๋Šฅํ•œ ๊ฐ’์€ ๋‹ค๋ฅธ ๊ฐ’์˜ ๊ฒฐ๊ณผ์ด๋ฏ€๋กœ ์ œํ’ˆ์ด๋ผ๊ณ ํ•ฉ๋‹ˆ๋‹ค. ๋งŽ์€ ์–ธ์–ด์—๋Š” ์ œํ’ˆ ํƒ€์ž…์˜ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ํ˜•์‹์ธ ํŠœํ”Œ ํƒ€์ž…์ด ์žˆ์Šต๋‹ˆ๋‹ค.

Set theory์„ ์ฐธ์กฐํ•˜์„ธ์š”.

Option

Option์€ Some๊ณผ None์œผ๋กœ ๋ถˆ๋ฆฌ๋Š” sum type์ž…๋‹ˆ๋‹ค.

Option์€ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

// Naive definition

const Some = (v) => ({
  val: v,
  map (f) {
    return Some(f(this.val))
  },
  chain (f) {
    return f(this.val)
  }
})

const None = () => ({
  map (f) {
    return this
  },
  chain (f) {
    return this
  }
})

// maybeProp :: (String, {a}) -> Option a
const maybeProp = (key, obj) => typeof obj[key] === 'undefined' ? None() : Some(obj[key])

Chain์„ Option์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์‹œํ€€์Šค ํ•จ์ˆ˜์— ์‚ฌ์šฉํ•˜์„ธ์š”.

// getItem :: Cart -> Option CartItem
const getItem = (cart) => maybeProp('item', cart)

// getPrice :: Item -> Option Number
const getPrice = (item) => maybeProp('price', item)

// getNestedPrice :: cart -> Option a
const getNestedPrice = (cart) => getItem(obj).chain(getPrice)

getNestedPrice({}) // None()
getNestedPrice({item: {foo: 1}}) // None()
getNestedPrice({item: {price: 9.99}}) // Some(9.99)

Option์€ Maybe๋ผ๊ณ ๋„ํ•ฉ๋‹ˆ๋‹ค. Some์€ ๋•Œ๋•Œ๋กœ Just๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. None์€ Nothing์ด๋ผ๊ณ ๋„ ํ•ฉ๋‹ˆ๋‹ค.

Functional Programming Libraries in JavaScript


P.S: ์ด ์ €์žฅ์†Œ๋Š” ํ›Œ๋ฅญํ•œ contributions ๋•๋ถ„์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].