June 05, 2021
λ³Έ ν¬μ€νΈλ fp-ts 곡μ λ¬Έμμ Learning Resourcesμ μλ Getting Startedμμ μκ°νλ λ¬Έμλ€μ λ²μνλ©° νμ΅ν λ¬Έμμ λλ€. μλ³Έ λ¬Έμλ λ§ν¬μμ νμΈν μ μμΌλ©° μμ±ν μ½λλ€μ μ¬κΈ°μμ νμΈν μ μμ΅λλ€.
μ§λ ν¬μ€νΈμμ μ°λ¦¬λ M
μ΄ Applicative μΈμ€ν΄μ€λ₯Ό μΈμ νλ€λ©΄ g
λ₯Ό λ€μ΄ μ¬λ¦ΌμΌλ‘μ¨ μμν n
ν νλ‘κ·Έλ¨ g
λ‘ μ΄ννΈ μλ νλ‘κ·Έλ¨ f: (a: A) => M<B>
λ₯Ό ꡬμ±ν μ μμμ 보μμ΅λλ€.
νλ‘κ·Έλ¨ f | νλ‘κ·Έλ¨ g | μ‘°ν© |
---|---|---|
μμν | μμν | g β f |
μ΄ννΈ μλ | μμν, n ν |
liftAn(g) β f |
κ·Έλ¬λ λ§μ§λ§ ν κ°μ§ κ²½μ°λ₯Ό ν΄κ²°ν΄μΌ ν©λλ€. λ νλ‘κ·Έλ¨μ΄ λͺ¨λ μ΄ννΈκ° μλ€λ©΄ μ΄λ»κ² ν μ μμκΉμ?
f: (a: A) => M<B>
g: (b: B) => M<C>
κ·Έλ° f
μ g
μ βμ‘°ν©βμ 무μμΌκΉμ?
μ΄ λ§μ§λ§ κ²½μ°λ₯Ό μ²λ¦¬νκΈ° μν΄μλ μ€μ²©λ 컨ν
μ€νΈλ‘ λλκΈ° μ½κΈ° λλ¬Έμ Functor
λ³΄λ€ λ κ°λ ₯ν κ²μ΄ νμν©λλ€.
λ λ§μ κ²μ΄ νμν μ΄μ λ₯Ό λ μ μ€λͺ νκΈ° μν΄ λͺ κ°μ§ μμλ₯Ό μ΄ν΄λ³΄κ² μ΅λλ€.
M = Array
)νΈμν° μ¬μ©μμ νλ‘μλ₯Ό κ²μνκ³ μΆλ€κ³ κ°μ ν΄ λ³΄κ² μ΅λλ€.
interface User {
followers: Array<User>;
}
const getFollowers = (user: User): Array<User> => user.followers;
const followersOfFollowers = (user: User): Array<Array<User>> =>
getFollowers(user).map(getFollowers);
λκ° μλͺ»λ κ²μ΄ μμ΅λλ€. followersOfFollowers
ν¨μμ λ°ν νμ
μ Array<Array<User>>
μ΄μ§λ§ μ°λ¦¬λ Array<User>
νμ
μ λ°ννκΈ°λ₯Ό μν©λλ€.
μ°λ¦¬λ μ€μ²©λ λ°°μ΄μ νννκ² λ§λ€μ΄μΌ ν©λλ€.
fp-ts
μμ μ 곡νλ flatten: <A>(mma: Array<Array<A>>) => Array<A>
ν¨μλ₯Ό μ¬μ©νλ©΄ νΈλ¦¬ν©λλ€.
import { flatten } from 'fp-ts/lib/Array';
const followersOfFollowers = (user: User): Array<User> =>
flatten(getFollowers(user).map(getFollowers));
μ’μ΅λλ€. λ€λ₯Έ λ°μ΄ν° ꡬ쑰λ μ΄λ¨κΉμ?
M = Option
)μ«μ λͺ©λ‘μ κ°μ₯ μμ λ°μ΄ν°μ μμλ₯Ό κ³μ°νκ³ μΆλ€κ³ κ°μ ν΄ λ³΄κ² μ΅λλ€.
import { Option, some, none, option } from 'fp-ts/lib/Option';
import { head } from 'fp-ts/lib/Array';
const inverse = (n: number): Option<number> => (n === 0 ? none : some(1 / n));
const inverseHead = (arr: Array<number>): Option<Option<number>> =>
option.map(head(arr), inverse);
λ€μ νλ² λ°μνμ΅λλ€. inverseHead
ν¨μλ Option<Option<number>>
νμ
μ λ°ννκ³ μμ§λ§ μ°λ¦¬λ Option<number>
νμ
μ΄ λ°νλκΈ°λ₯Ό μν©λλ€.
μ°λ¦¬λ μ€μ²©λ Option
μ νννκ² λ§λ€μ΄μΌ ν©λλ€.
import { isNone } from 'fp-ts/lib/Option';
const flatten = <A>(mma: Option<Option<A>>): Option<A> =>
isNone(mma) ? none : mma.value;
const inverseHead = (arr: Array<number>): Option<number> =>
flatten(option.map(head(arr), inverse));
λͺ¨λ flatten
ν¨μλ€μ μ°μ°ν μκΈ΄ κ²μ΄ μλλλ€. μ΄κ²λ€μ λͺ¨λ μμ ν¨μνμ μΈ ν¨ν΄μ΄ μ‘΄μ¬ν©λλ€.
μ€μ λ‘ μ΄λ¬ν λͺ¨λ νμ μμ±μ(λ° κΈ°ν λ§μ μμ±μ)λ Monad μΈμ€ν΄μ€λ₯Ό νμ©νκ³ μμ΅λλ€.
flatten
μ Monadμ κ°μ₯ κ³ μ ν κΈ°λ₯μ λλ€.
κ·Έλμ Monadλ 무μμΈκ°μ?
μλ λ΄μ©μ΄ Monadκ° μμ£Ό μ μλλ λ°©μμ λλ€.
Monadλ μλμ κ°μ΄ μΈ κ°μ§λ‘ μ μλ©λλ€.
(1) Functor μΈμ€ν΄μ€λ₯Ό νμ©νλ νμ
μμ±μ M
(2) μλμ μκ·Έλμ²λ₯Ό κ°λ of
ν¨μ
of: <A>(a: A) => HKT<M, A>
(3) μλ μκ·Έλμ²λ₯Ό κ°λ flatMap
ν¨μ
flatMap: <A, B>(f: (a: A) => HKT<M, B>) => ((ma: HKT<M, A>) => HKT<M, B>)
HKT
νμ μ μ λ€λ¦ νμ μμ±μλ₯Ό λνλ΄λfp-ts
λ°©μμ΄λ©°HKT<M, X>
λ νμX
μ μ μ©λ νμ μμ±μM
(μ¦,M<X>
)μ μκ°ν μ μμ΅λλ€.
of
μ flatMap
ν¨μλ μλμ μΈ κ°μ§ 쑰건μ λ§μ‘±ν΄μΌ ν©λλ€.
flatMap(of) β f = f
flatMap(f) β of = f
flatMap(h) β (flatMap(g) β f) = flatMap((flatMap(h) β g)) β f
μ¬κΈ°μ f
, g
, h
λ λͺ¨λ μ΄ννΈκ° μλ ν¨μμ΄κ³ β
λ μΌλ°μ μΈ ν¨μ μ‘°ν©μ
λλ€.
μ΄λ° μ μλ₯Ό μ²μ 보μμ λ 첫 λ°μμ λΉν©νμ΅λλ€.
μλμ λͺ¨λ μ§λ¬Έμ΄ λ΄ λ¨Έλ¦Ώμμμ 맴λκ³ μμμ΅λλ€.
flatten
μ μ΄λμ μμκΉμ?μ΄ ν¬μ€νΈμμλ κ° μ§λ¬Έμ λν λ΅λ³μ μλν©λλ€.
λ¬Έμ λ‘ λμκ° λ³΄κ² μ΅λλ€. λ κ°μ μ΄ννΈ μλ ν¨μ(Kleisli arrowsλΌκ³ λ ν¨)μ μ‘°ν©μ 무μμ λκΉ?
λλ κ·Έκ²μ νμ μ΄ λ¬΄μμΈμ§μ‘°μ°¨ λͺ¨λ¦ λλ€.
μ κΉβ¦ μ°λ¦¬λ μ΄λ―Έ μ‘°ν©μ κ΄ν μΆμνλ₯Ό λ§λ¬μ΅λλ€. μΉ΄ν κ³ λ¦¬μ κ΄ν΄ μκΈ°ν κ²μ κΈ°μ΅νκ³ μμ΅λκΉ?
μΉ΄ν κ³ λ¦¬λ μ‘°ν©μ λ³Έμ§μ ν¬μ°©ν©λλ€.
μ°λ¦¬λ μ΄ λ¬Έμ λ₯Ό μΉ΄ν κ³ λ¦¬ λ¬Έμ λ‘ λ°κΏ μ μμ΅λλ€. Kleisli arrowsμ μ‘°ν© λͺ¨λΈμ λ§λ μΉ΄ν κ³ λ¦¬λ₯Ό μ°Ύμ μ μμ΅λκΉ?
μ΄ννΈ μλ ν¨μλ§ ν¬ν¨νλ μΉ΄ν κ³ λ¦¬ K (Kleisli μΉ΄ν κ³ λ¦¬)λ₯Ό ꡬμ±ν΄ λ³΄κ² μ΅λλ€.
f: A βΌ M<B>
κ° μμΌλ©΄ Kμμ νμ΄ν f': A βΌ B
βλ₯Ό 그립λλ€.κ·Έλ λ€λ©΄ Kμμ f'
μ g'
μ μ‘°ν©μ μλ μ΄λ―Έμ§μμ h'
λΌκ³ νμλ μ μ νμ΄νμ
λλ€.
h'
λ A
μμ C
λ‘ μ΄λνλ νμ΄νμ΄λ―λ‘ TSμλ A
μμ M<C>
κΉμ§μ ν΄λΉνλ ν¨μ h
κ° μμ΄μΌ ν©λλ€.
λ°λΌμ TSμμ f
μ g
μ μ‘°ν©μ λν μ’μ ν보λ μ¬μ ν (a: A) => M <C>
μ μκ·Έλμ²λ₯Ό κ°μ§ μ΄ννΈ μλ ν¨μμ
λλ€.
κ·Έλ¬ν ν¨μλ₯Ό μ΄λ»κ² κ΅¬μ± ν μ μμκΉμ?
Monad μ μμ μμ (1)μ M
μ΄ Functor μΈμ€ν΄μ€λ₯Ό νμ©νλ―λ‘ ν¨μ g: (b: B) => M<C>
λ₯Ό ν¨μ lift(g): (mb: M<B>) => M<M<C>>
λ‘ λ€μ΄ μ¬λ¦΄ μ μμ΅λλ€. (μ¬κΈ°μλ λμμ΄ map
μ μ¬μ©νκ³ μμ΅λλ€.)
flatMap
μ μ¬κΈ°μ μ λλλ€.
κ·Έλ¦¬κ³ μ΄μ μ°λ¦¬λ λ§νμ΅λλ€. M<M<C>>
νμ
μ κ°μ M<C>
νμ
μ κ°μΌλ‘ νννκ² λ§λ€ μ μλ Functor μΈμ€ν΄μ€μ λν κΈ°λ₯μ΄ μμ΅λλ€. μΆκ°μ μΈ flatten κΈ°λ₯μ΄ νμν©λλ€.
μ΄λ¬ν κΈ°λ₯μ μ μ ν μ μλ€λ©΄ μ°Ύκ³ μλ μ‘°ν©μ μ»μ μ μμ΅λλ€.
h = flatten β map(g) β f
νμ§λ§ flatten β map(g)
μ flatMapμ
λλ€. μ΄λ¦μ μ¬κΈ°μμ μ λλμμ΅λλ€!
h = flatMap(g) β f
μ΄μ βμ‘°ν©νβλ₯Ό μ λ°μ΄νΈ ν μ μμ΅λλ€.
νλ‘κ·Έλ¨ f | νλ‘κ·Έλ¨ g | μ‘°ν© |
---|---|---|
μμν | μμν | g β f |
μ΄ννΈ μλ | μμν, n ν |
liftAn(g) β f |
μ΄ννΈ μλ | μ΄ννΈ μλ | flatMap(g) β f |
of
λ μ΄λ€κ°μ? of
λ Kμ νλ±μ± ννμμ μ λλ©λλ€. Kμ κ° νλ±μ± ννμ λν΄ A
μμ M<A>
κΉμ§μ ν΄λΉνλ ν¨μκ° μμ΄μΌ ν©λλ€ (μ¦, of: <A>(a: A) => M<A>
)
of
λ μ¬κΈ°μ μ λλλ€.
λ§μ§λ§ μ§λ¬Έ: λ²μΉμ μ΄λμμ μμ΅λκΉ? κ·Έκ²λ€μ TSλ‘ λ²μλ Kμ μΉ΄ν κ³ λ¦¬ λ²μΉμΌ λΏμ λλ€.
λ²μΉ | K | TS |
---|---|---|
μΌμͺ½ νλ±μ (Left identity) | 1B β fβ = fβ |
flatMap(of) β f = f |
μ€λ₯Έμͺ½ νλ±μ (Right identity) | f' β 1A = f' |
flatMap(f) β of = f |
κ²°ν© λ²μΉ (Associativity ) | h' β (g' β f') = (h' β g') β f' |
flatMap(h) β (flatMap(g) β f) = flatMap((flatMap(h) β g)) β f |
fp-ts
μ Monadfp-ts
μμ flatMap
ν¨μλ chain
μ΄λΌλ λ³νμ μν΄ λͺ¨λΈλ§ λλ©°, κΈ°λ³Έμ μΌλ‘ μΈμκ° μ¬λ°°μ΄ λ flatMap
μ
λλ€.
flatMap: <A, B>(f: (a: A) => HKT<M, B>) => ((ma: HKT<M, A>) => HKT<M, B>)
chain: <A, B>(ma: HKT<M, A>, f: (a: A) => HKT<M, B>) => HKT<M, B>
μ°Έκ³ :
chain
μflatMap
μμ νμλ μ μμΌλ©° λ°λλ κ°λ₯νλ€.
μ΄μ μ€μ²©λ 컨ν
μ€νΈμ λ¬Έμ λ₯Ό 보μ¬μ£Όλ μμ λ‘ λμκ° chain
μ μ¬μ©νμ¬ λ¬Έμ λ₯Ό ν΄κ²°ν μ μμ΅λλ€.
μλ¬Έμμλ
Option.chain
κ³ΌArray
μchain
μ μ¬μ©νλΌκ³ μμ±λμ΄ μμ§λ§, μ΅μ λ²μ μ fp-tsμμλ deprecated λμ΄ μμΌλ©°arrayChain
κ³ΌoptionChain
μ μ¬μ©νλ©΄ λ©λλ€.
import type { Option } from 'fp-ts/lib/Option';
import { chain as arrayChain, head } from 'fp-ts/lib/Array';
import { chain as optionChain } from 'fp-ts/lib/Option';
const followersOfFollowers = (user: User): Array<User> =>
arrayChain(getFollowers)(getFollowers(user));
const headInverse = (arr: Array<number>): Option<number> =>
optionChain(inverse)(head(arr));
ν¨μν νλ‘κ·Έλλ°μ μ΄ννΈλ‘ ν¨μλ₯Ό μ‘°ν©νλ 보νΈμ μΈ λ°©λ²μ μ 곡ν©λλ€. Functor, Applicative Functor λ° Monadλ λͺ¨λ λ€λ₯Έ μ’ λ₯μ νλ‘κ·Έλ¨μ μ‘°ν©νκΈ° μν μμΉμ μΈ λꡬλ₯Ό μ 곡νλ μΆμνμ λλ€.
μμ½ : ν¨μν νλ‘κ·Έλλ°μ μ€μ λ‘ μ‘°ν©μ κ΄ν κ²μ λλ€.