[λ²ˆμ—­] ν”„λ‘œκ·Έλž˜λ¨Έλ₯Ό μœ„ν•œ μΉ΄ν…Œκ³ λ¦¬ 이둠 - 7. νŽ‘ν„°

    [λ²ˆμ—­] ν”„λ‘œκ·Έλž˜λ¨Έλ₯Ό μœ„ν•œ μΉ΄ν…Œκ³ λ¦¬ 이둠 - 7. νŽ‘ν„°


    이번 μ±•ν„°μ—μ„œλŠ” νŽ‘ν„°(Functor)에 λŒ€ν•΄μ„œ 이야기λ₯Ό 해보렀고 ν•œλ‹€. νŽ‘ν„°λŠ” κ°„λ‹¨ν•˜μ§€λ§Œ 맀우 κ°•λ ₯ν•œ κ°œλ…μ΄λ©° μΉ΄ν…Œκ³ λ¦¬ 이둠은 이처럼 κ°„λ‹¨ν•˜μ§€λ§Œ κ°•λ ₯ν•œ μ•„μ΄λ””μ–΄λ‘œ 가득 μ°¨μžˆλ‹€.

    νŽ‘ν„°λŠ” μΉ΄ν…Œκ³ λ¦¬ κ°„μ˜ 맀핑이닀. 즉, 두 μΉ΄ν…Œκ³ λ¦¬ CC와 DDκ°€ μ£Όμ–΄μ‘Œμ„ λ•Œ, νŽ‘ν„° FFλŠ” μΉ΄ν…Œκ³ λ¦¬ CC의 λŒ€μƒμ„ μΉ΄ν…Œκ³ λ¦¬ DD의 λŒ€μƒμœΌλ‘œ λ§€ν•‘ν•˜λŠ” 것이며, κ²°κ΅­ λŒ€μƒλ“€μ— λŒ€ν•œ ν•¨μˆ˜λΌκ³  λ³Ό 수 μžˆλ‹€.

    λ§Œμ•½ μΉ΄ν…Œκ³ λ¦¬ CC의 λŒ€μƒμ„ aa라고 ν•œλ‹€λ©΄, μš°λ¦¬λŠ” λ§€ν•‘λœ μΉ΄ν…Œκ³ λ¦¬ DD의 λŒ€μƒμ„ FaF a라고 ν‘œν˜„ν•  수 μžˆλ‹€. ν•˜μ§€λ§Œ μΉ΄ν…Œκ³ λ¦¬λŠ” λŒ€μƒμœΌλ‘œλ§Œ κ΅¬μ„±λ˜μ–΄μžˆμ§€ μ•ŠμœΌλ©° λŒ€μƒκ³Ό 그듀을 μ—°κ²°ν•˜λŠ” μ‚¬μƒκΉŒμ§€ λͺ¨λ‘ ν¬ν•¨ν•˜λŠ” κ°œλ…μ΄λ‹€. 즉, νŽ‘ν„°λŠ” λŒ€μƒ 뿐 μ•„λ‹ˆλΌ 사상 λ˜ν•œ λ§€ν•‘ν•˜λ©°, 이런 κ²½μš°μ—λŠ” 사상에 λŒ€ν•œ ν•¨μˆ˜λΌκ³  λ³Ό μˆ˜λ„ μžˆλ‹€. κ·Έλ ‡λ‹€κ³  νŽ‘ν„°κ°€ λ§ˆμŒλŒ€λ‘œ 사상을 λ§€ν•‘ν•˜λŠ” 것은 μ•„λ‹ˆκ³ , λ°˜λ“œμ‹œ μ‚¬μƒμœΌλ‘œ μ—°κ²°λœ λŒ€μƒλ“€μ˜ ꡬ쑰λ₯Ό κ·ΈλŒ€λ‘œ 보쑴해야 ν•œλ‹€.

    λ§Œμ•½ μΉ΄ν…Œκ³ λ¦¬ CC에 μžˆλŠ” 사상 ffκ°€ λŒ€μƒ aa와 bbλ₯Ό μ—°κ²°ν•˜κ³  μžˆλ‹€λ©΄,

    f :: a -> b

    νŽ‘ν„°λ₯Ό 톡해 λ§€ν•‘λœ μΉ΄ν…Œκ³ λ¦¬ DD에 μžˆλŠ” 사상 ff인 FfF f λ˜ν•œ CC와 λ™μΌν•œ ꡬ쑰둜 λŒ€μƒ FaF a와 FbF bλ₯Ό μ—°κ²°ν•΄μ•Ό ν•œλ‹€.

    F f :: F a -> F b
    1

    μ—¬κΈ°μ„œ μ•Œ 수 μžˆλ“―μ΄ νŽ‘ν„°λŠ” μΉ΄ν…Œκ³ λ¦¬μ˜ ꡬ쑰λ₯Ό κ·ΈλŒ€λ‘œ λ³΄μ‘΄ν•œλ‹€. ν•œ μΉ΄ν…Œκ³ λ¦¬ λ‚΄μ—μ„œ μ—°κ²°λ˜μ–΄μžˆλŠ” 것듀은 νŽ‘ν„°λ₯Ό 톡해 λ§€ν•‘λœ μΉ΄ν…Œκ³ λ¦¬μ—μ„œλ„ κ·ΈλŒ€λ‘œ μ—°κ²°λ˜μ–΄ μžˆλŠ” 것이닀. 이에 더해 μΉ΄ν…Œκ³ λ¦¬μ—λŠ” λ‹¨μˆœνžˆ λŒ€μƒμ„ μ‚¬μƒμœΌλ‘œ μ—°κ²°ν•˜λŠ” 것 뿐 μ•„λ‹ˆλΌ 사상 κ°„μ˜ ν•©μ„±μ΄λΌλŠ” κ°œλ… λ˜ν•œ μ‘΄μž¬ν•œλ‹€.

    사상 hhκ°€ ff와 gg의 합성사상인 상황을 μƒκ°ν•΄λ³΄μž.

    h = g . f

    κ·Έλ ‡λ‹€λ©΄ νŽ‘ν„° FFλ₯Ό 톡해 λ§Œλ“€μ–΄μ§„ μΉ΄ν…Œκ³ λ¦¬μ˜ μ‚¬μƒλ“€μ˜ ν•©μ„± 관계도 이와 λ™μΌν•˜κ²Œ μœ μ§€λœλ‹€.

    F h = F g . F f
    2

    λ§ˆμ§€λ§‰μœΌλ‘œ μš°λ¦¬λŠ” μΉ΄ν…Œκ³ λ¦¬ CC μ•ˆμ˜ λͺ¨λ“  항등사상듀이 μΉ΄ν…Œκ³ λ¦¬ DD의 ν•­λ“±μ‚¬μƒλ“€λ‘œ λ§€ν•‘λ˜λŠ” 상황 λ˜ν•œ 생각해볼 수 μžˆλ‹€.

    F id_a = id_Fa

    μ—¬κΈ°μ„œ idaid_aλŠ” λŒ€μƒ aa의 항등사상이며, idFaid_FaλŠ” λŒ€μƒ FaF a의 항등사상이닀. 이처럼 항상 μΉ΄ν…Œκ³ λ¦¬μ˜ ꡬ쑰λ₯Ό λ³΄μ‘΄ν•΄μ•Όν•œλ‹€λŠ” μ‘°κ±΄λ“€λ‘œ 인해 νŽ‘ν„°λŠ” 일반적인 ν•¨μˆ˜λ³΄λ‹€ 더 μ œν•œμ μΈ κ°œλ…μ΄ λœλ‹€.

    3

    λ§Œμ•½ μ–΄λ–€ μΉ΄ν…Œκ³ λ¦¬λ₯Ό 각 λŒ€μƒλ“€μ΄ μ‚¬μƒμœΌλ‘œ μ—°κ²°λ˜μ–΄μžˆλŠ” μΌμ’…μ˜ λ„€νŠΈμ›Œν¬λΌκ³  μƒμƒν•΄λ³΄μž. νŽ‘ν„°λŠ” 각 λŒ€μƒκ³Ό 사상을 κ·Έμ € λ§€ν•‘ν•˜λŠ” κ°œλ…μΌλΏμ΄λ―€λ‘œ λ„€νŠΈμ›Œν¬μ˜ μš”μ†Œλ“€μ„ μ„œλ‘œ λΆ„λ¦¬ν•΄λ‚΄λŠ” 것은 μ ˆλŒ€ λΆˆκ°€λŠ₯ν•˜λ‹€. νŽ‘ν„°λ‘œ λŒ€μƒλ“€μ„ ν•©μΉ  μˆ˜λ„ 있고 μ—¬λŸ¬ 사상듀을 ν•˜λ‚˜λ‘œ λΆ™ν˜€λ†“μ„ μˆ˜λ„ μžˆκ² μ§€λ§Œ λΆ„ν•΄ν•˜λŠ” 것은 λΆˆκ°€λŠ₯ν•œ 것이닀.

    이와 같이 λΆ„ν•΄κ°€ λΆˆκ°€λŠ₯ν•˜λ‹€λŠ” μ œμ•½μ€ λ―Έμ λΆ„μ—μ„œ λ³Ό 수 μžˆλŠ” μ—°μ†μ„±μ˜ 쑰건과 μœ μ‚¬ν•˜λ‹€. 이런 μ˜λ―Έμ—μ„œ νŽ‘ν„°λŠ” β€œμ—°μ†μ β€μ΄λΌκ³  λ³Ό μˆ˜λ„ μžˆλ‹€. λ˜ν•œ 마치 μ •μ˜μ—­κ³Ό κ³΅μ—­μ˜ 관계λ₯Ό μΆ•μ†Œλ‚˜ ν¬ν•¨μ΄λΌλŠ” μ‹œκ°μœΌλ‘œ λ°”λΌλ³΄λŠ” 것과 μœ μ‚¬ν•˜κ²Œ νŽ‘ν„° λ˜ν•œ λ™μΌν•œ μ‹œκ°μœΌλ‘œ 바라볼 수 μžˆλ‹€. 예λ₯Ό λ“€λ©΄ νŽ‘ν„°μ˜ μ†ŒμŠ€κ°€ λ˜λŠ” μΉ΄ν…Œκ³ λ¦¬κ°€ λŒ€μƒ μΉ΄ν…Œκ³ λ¦¬λ³΄λ‹€ 더 μž‘μ„ λ•ŒλŠ” 포함과 같은 κ°œλ…μœΌλ‘œ λ³Ό 수 μžˆλŠ” 것이닀.

    ν•œλ²ˆ 극단적인 μ˜ˆμ‹œλ₯Ό μƒκ°ν•΄λ³΄μž. νŽ‘ν„°μ˜ μ†ŒμŠ€κ°€ λ˜λŠ” μΉ΄ν…Œκ³ λ¦¬λŠ” 단 ν•˜λ‚˜μ˜ λŒ€μƒκ³Ό ν•˜λ‚˜μ˜ ν•­λ“±μ‚¬μƒλ§ŒμœΌλ‘œ 이루어진 λ‹¨μΌλŒ€μƒ μΉ΄ν…Œκ³ λ¦¬κ°€ 될 μˆ˜λ„ μžˆλ‹€. 이처럼 λ‹¨μΌλŒ€μƒ μΉ΄ν…Œκ³ λ¦¬λ₯Ό λ‹€λ₯Έ μΉ΄ν…Œκ³ λ¦¬λ‘œ λ§€ν•‘ν•˜λŠ” νŽ‘ν„°λŠ” λ‹¨μˆœνžˆ ν•΄λ‹Ή μΉ΄ν…Œκ³ λ¦¬μ—μ„œ ν•˜λ‚˜μ˜ λŒ€μƒμ„ μ„ νƒν•˜λŠ” 것과 λ‹€λ₯Ό 게 μ—†λ‹€. μ΄λŠ” λ‹¨μΌμ›μ†Œμ§‘ν•©μ—μ„œ ν•˜λ‚˜μ˜ μ›μ†Œλ₯Ό μ„ νƒν•˜λŠ” μ‚¬μƒμ˜ νŠΉμ„±κ³Ό μ™„μ „νžˆ μœ μ‚¬ν•˜λ‹€.

    μ†ŒμŠ€ μΉ΄ν…Œκ³ λ¦¬λ₯Ό μ΅œλŒ€ν•œ μΆ•μ†Œν•˜λŠ” νŽ‘ν„°λŠ” μƒμˆ˜ νŽ‘ν„°(Constant Functor), Ξ”c\Delta c둜 λΆˆλ¦°λ‹€. 이 νŽ‘ν„°λŠ” μ†ŒμŠ€ μΉ΄ν…Œκ³ λ¦¬μ˜ λͺ¨λ“  λŒ€μƒμ„ λŒ€μƒ μΉ΄ν…Œκ³ λ¦¬μ—μ„œ μ„ νƒλœ ν•˜λ‚˜μ˜ λŒ€μƒμΈ cc둜, 그리고 μ†ŒμŠ€ μΉ΄ν…Œκ³ λ¦¬μ˜ λͺ¨λ“  사상을 항등사상인 idcid_c둜 λ§€ν•‘ν•œλ‹€. 이 νŽ‘ν„°λŠ” 마치 λΈ”λž™ν™€μ²˜λŸΌ μž‘λ™ν•˜μ—¬ λͺ¨λ“  것을 ν•˜λ‚˜μ˜ 특이점으둜 압좕해버린닀. μš°λ¦¬κ°€ μΆ”ν›„ ν•œκ³„(Limits)와 κ³΅ν•œκ³„(Colimits)에 λŒ€ν•΄ 이야기할 λ•Œ μ΄λŸ¬ν•œ νŽ‘ν„°λ₯Ό 더 μžμ„Ένžˆ μ‚΄νŽ΄λ³Ό 것이닀.

    πŸ’‘ μ—­μ£Ό

    μž‘κ°€μ˜ μ„€λͺ…λ§Œ 보면 마치 μƒμˆ˜ νŽ‘ν„°(Ξ”c\Delta c)κ°€ μ–΄λ–€ μΉ΄ν…Œκ³ λ¦¬λ₯Ό λ‹€λ₯Έ μΉ΄ν…Œκ³ λ¦¬λ‘œ λ§€ν•‘ν•˜λ©° μ••μΆ•ν•˜λŠ” λ…€μ„μœΌλ‘œ 보일 수 μžˆμ§€λ§Œ, 사싀 μƒμˆ˜ νŽ‘ν„°λŠ” νŠΉμ • μΉ΄ν…Œκ³ λ¦¬λ₯Ό 자기 μžμ‹ μœΌλ‘œ λ§€ν•‘ν•˜λŠ” μ—”λ„νŽ‘ν„°(EndoFunctor)이닀. (μ—”λ„νŽ‘ν„°μ— λŒ€ν•œ μ„€λͺ…은 λ°”λ‘œ λ‹€μŒ μ„Ήμ…˜μ— λ‚˜μ˜¨λ‹€.)

    즉, μƒμˆ˜ νŽ‘ν„°λŠ” μ–΄λ–€ μΉ΄ν…Œκ³ λ¦¬μ˜ λͺ¨λ“  λŒ€μƒμ„ κ·Έ μΉ΄ν…Œκ³ λ¦¬ λ‚΄μ˜ λŒ€μƒ ν•˜λ‚˜λ‘œ 맀핑(μ••μΆ•)ν•˜λŠ” κ°œλ…μ΄λΌκ³  보면 λœλ‹€. 이 κ³Όμ •μ—μ„œ μΉ΄ν…Œκ³ λ¦¬μ˜ λŒ€μƒλ“€μ„ μ—°κ²°ν•˜λ˜ μž„μ˜μ˜ 사상듀은 λͺ¨λ‘ μ„ νƒλœ λŒ€μƒμ˜ ν•­λ“± μ‚¬μƒμœΌλ‘œ λ§€ν•‘λœλ‹€.

    7.1 ν”„λ‘œκ·Έλž˜λ°μ—μ„œμ˜ νŽ‘ν„°

    μ΄μ œλΆ€ν„°λŠ” μ‹€μ§ˆμ μΈ ν”„λ‘œκ·Έλž˜λ°μ— λŒ€ν•΄μ„œ μ΄μ•ΌκΈ°ν•΄λ³΄μž. 이미 μš°λ¦¬λŠ” ν”„λ‘œκ·Έλž˜λ°μ˜ μ„Έκ³„μ—μ„œ νƒ€μž…κ³Ό ν•¨μˆ˜λ‘œ 이루어진 μΉ΄ν…Œκ³ λ¦¬λ₯Ό 닀루고 μžˆλ‹€. μ΄λ²ˆμ—λŠ” 이 μΉ΄ν…Œκ³ λ¦¬λ₯Ό 자기 μžμ‹ μœΌλ‘œ λ§€ν•‘ν•˜λŠ” νŽ‘ν„°, μ—”λ„νŽ‘ν„°(EndoFunctor)에 λŒ€ν•΄μ„œ 생각해보렀고 ν•œλ‹€.

    자, νƒ€μž…μœΌλ‘œ 이루어진 μΉ΄ν…Œκ³ λ¦¬μ—μ„œμ˜ μ—”λ„νŽ‘ν„°λŠ” λ¬΄μ—‡μΌκΉŒ? 이 νŽ‘ν„°λŠ” νƒ€μž…μ„ νƒ€μž…μœΌλ‘œ λ§€ν•‘ν•˜λŠ” νŽ‘ν„°μΌ 것이닀. 사싀 μ—¬λŸ¬λΆ„μ€ 이미 μ΄λŸ¬ν•œ λ§€ν•‘μ˜ μ˜ˆμ‹œλ₯Ό μˆ±ν•˜κ²Œ λ΄μ™”μ„ν…Œμ§€λ§Œ 단지 그것이 μ—”λ„νŽ‘ν„°λΌλŠ” 것을 깨닿지 λͺ» ν–ˆμ„ 뿐이닀. μ•„λž˜ λͺ‡ 가지 μ˜ˆμ‹œλ₯Ό ν•œλ²ˆ μ‚΄νŽ΄λ³΄μž.

    7.1.1 Maybe νŽ‘ν„°

    Maybe의 μ •μ˜λŠ” μ–΄λ– ν•œ νƒ€μž… aλ₯Ό Maybe aλΌλŠ” νƒ€μž…μœΌλ‘œ λ§€ν•‘ν•˜λŠ” 것이닀.

    data Maybe a = Nothing | Just a

    μ—¬κΈ°μ„œ μ€‘μš”ν•œ ν¬μΈνŠΈκ°€ ν•˜λ‚˜ μžˆλ‹€. Maybe μžμ²΄λŠ” νƒ€μž…μ΄ μ•„λ‹ˆλΌ νƒ€μž… μƒμ„±μž(Type Constructor)λΌλŠ” 것이닀. νƒ€μž… μƒμ„±μžλ₯Ό νƒ€μž…μœΌλ‘œ λ³€ν™˜ν•˜κΈ° μœ„ν•΄μ„œλŠ” Intλ‚˜ Boolκ³Ό 같은 νƒ€μž… 인자λ₯Ό μƒμ„±μžμ—κ²Œ μ œκ³΅ν•΄μ•Ό ν•œλ‹€. 즉, 아무 인자둜 받지 μ•ŠλŠ” MaybeλŠ” νƒ€μž…μ— λŒ€ν•œ ν•¨μˆ˜λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 것이닀.

    κ·Έλ ‡λ‹€λ©΄ Maybeλ₯Ό νŽ‘ν„°λ‘œ λ³€ν™˜ν•΄λ³Ό 수 μžˆμ„κΉŒ? (ν”„λ‘œκ·Έλž˜λ° λ§₯λ½μ—μ„œ νŽ‘ν„°μ— λŒ€ν•΄ 이야기할 λ•ŒλŠ” 거의 항상 μ—”λ„νŽ‘ν„°λ₯Ό μ΄μ•ΌκΈ°ν•œλ‹€λŠ” 사싀을 μœ λ…ν•˜μž.) νŽ‘ν„°λŠ” λŒ€μƒ(νƒ€μž…)의 맀핑 뿐만 μ•„λ‹ˆλΌ 사상(ν•¨μˆ˜)의 맀핑도 λͺ¨λ‘ ν¬ν•¨ν•˜λŠ” κ°œλ…μ΄λ‹€. ν•œλ²ˆ aμ—μ„œ b둜 λ‚˜μ•„κ°€λŠ” μž„μ˜μ˜ ν•¨μˆ˜ fκ°€ μžˆλ‹€κ³  μƒκ°ν•΄λ³΄μž.

    f :: a -> b

    μš°λ¦¬λŠ” Maybe aμ—μ„œ Maybe b둜 λ‚˜μ•„κ°€λŠ” ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜κ³  μ‹Άλ‹€. μ΄λŸ¬ν•œ ν•¨μˆ˜λ₯Ό μ œλŒ€λ‘œ μ •μ˜ν•˜κΈ° μœ„ν•΄μ„œλŠ” Maybeλ₯Ό κ΅¬μ„±ν•˜λŠ” 두 μƒμ„±μžμΈ Nothingκ³Ό Just에 λŒ€ν•΄ κ³ λ €ν•΄μ•Όν•œλ‹€.

    Nothing의 κ²½μš°μ—λŠ” κ·Έμ € Nothing을 λ°˜ν™˜ν•΄μ£ΌκΈ°λ§Œ ν•˜λ©΄ λ˜λ‹ˆ κ°„λ‹¨ν•˜λ‹€. 그리고 μΈμžκ°€ Just인 κ²½μš°μ—λŠ” ν•¨μˆ˜ fλ₯Ό Justκ°€ 가지고 μžˆλŠ” 값에 μ μš©ν•΄μ£Όλ©΄ 될 것이닀.

    즉, MaybeλΌλŠ” νŽ‘ν„°λ₯Ό 거친 f의 λͺ¨μŠ΅μ€ μ•„λž˜μ™€ 같은 ν•¨μˆ˜κ°€ λœλ‹€. (Haskellμ—μ„œλŠ” λ³€μˆ˜λͺ…에 μ•„ν¬μŠ€νŠΈλ‘œν”Ό(’)λ₯Ό μ‚¬μš©ν•  수 있으며, 이 κΈ°λŠ₯은 μ•„λž˜μ™€ 같은 κ²½μš°μ— 맀우 νŽΈλ¦¬ν•˜λ‹€.)

    f' :: Maybe a -> Maybe b
    f' Nothing = Nothing
    f' (Just x) = Just (f x)

    Haskellμ—μ„œλŠ” νŽ‘ν„°κ°€ 사상을 λ§€ν•‘ν•˜λŠ” ν–‰μœ„λ₯Ό fmapμ΄λΌλŠ” κ³ μ°¨ν•¨μˆ˜λ‘œ κ΅¬ν˜„ν•˜λ©°, Maybe의 κ²½μš°μ—λŠ” μ•„λž˜μ™€ 같은 μ •μ˜κ°€ 될 것이닀.

    fmap :: (a -> b) -> (Maybe a -> Maybe b)
    4

    μš°λ¦¬λŠ” μ’…μ’… fmap이 ν•¨μˆ˜λ₯Ό λ¦¬ν”„νŠΈ(lift)ν•œλ‹€κ³  λ§ν•œλ‹€. μ΄λ ‡κ²Œ λ¦¬ν”„νŒ…λœ ν•¨μˆ˜λŠ” 이제 Maybe νƒ€μž…μ˜ 값에 μž‘μš©ν•˜λŠ” ν•¨μˆ˜κ°€ λ˜μ—ˆλ‹€.

    λ˜ν•œ 컀링(Currting)으둜 인해 μœ„ μ •μ˜λŠ” ν•˜λ‚˜μ˜ ν•¨μˆ˜ a β†’ bλ₯Ό λ°›μ•„μ„œ λ‹€λ₯Έ ν•¨μˆ˜ Maybe a β†’ Maybe bλ₯Ό λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜, 그리고 a β†’ b와 Maybe aλΌλŠ” 두 개의 인자λ₯Ό λ°›μ•„ Maybe bλ₯Ό λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜, 총 두 가지 λ°©μ‹μœΌλ‘œ 해석될 수 μžˆλ‹€.

    fmap :: (a -> b) -> Maybe a -> Maybe b

    μ΄λŸ¬ν•œ νŠΉμ„±λ“€μ„ 기반으둜 Maybe에 λŒ€ν•œ fmap을 κ΅¬ν˜„ν•΄λ³΄λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

    fmap _ Nothing = Nothing
    fmap f (Just x) = Just (f x)
    -- μ μš©ν•  ν•¨μˆ˜ f와 Just xλ₯Ό 인자둜 λ°›μ•„,
    -- Justκ°€ 가진 값에 ν•¨μˆ˜ fκ°€ 적용된 꼴인 Just (f x)λ₯Ό λ°˜ν™˜ν•œλ‹€.

    μš°λ¦¬κ°€ Maybe νƒ€μž… μƒμ„±μžμ™€ fmap ν•¨μˆ˜κ°€ νŽ‘ν„°λ₯Ό ν˜•μ„±ν•œλ‹€λŠ” 것을 보이기 μœ„ν•΄μ„œλŠ” fmap ν•¨μˆ˜κ°€ ν•­λ“±κ³Ό ν•©μ„±μ˜ κ°œλ…μ„ λ³΄μ‘΄ν•œλ‹€λŠ” 것을 증λͺ…ν•΄μ•Όν•œλ‹€. μ΄λŸ¬ν•œ 것듀을 β€œνŽ‘ν„° 법칙(The Functor Laws)β€μ΄λΌλŠ” κ±°μ°½ν•œ μ΄λ¦„μœΌλ‘œ λΆ€λ₯΄κΈ΄ ν•˜μ§€λ§Œ 사싀은 κ·Έμ € νŽ‘ν„°κ°€ μΉ΄ν…Œκ³ λ¦¬μ˜ ꡬ쑰λ₯Ό λ³΄μ‘΄ν•œλ‹€λŠ” 것을 보μž₯ν•΄μ•Όν•œλ‹€λŠ” μ˜λ―Έμ΄λ‹€.

    7.1.2 방정식 μΆ”λ‘ (Equational Reasoning)

    νŽ‘ν„° 법칙을 증λͺ…ν•˜κΈ° μœ„ν•΄ 방정식 μΆ”λ‘ (Equational Reasoning)을 μ‚¬μš©ν•΄λ³΄λ €κ³  ν•œλ‹€. μ΄λŠ” Haskellμ—μ„œ ν”ν•˜κ²Œ μ‚¬μš©λ˜λŠ” 증λͺ… 기법인데, Haskell ν•¨μˆ˜κ°€ μ’Œλ³€μ΄ μš°λ³€κ³Ό κ°™λ‹€λŠ” 동등성(Equality)으둜 μ •μ˜λœλ‹€λŠ” 사싀을 μ΄μš©ν•˜λŠ” 기법이닀. μ–΄λ–€ μ½”λ“œλ₯Ό λ™μž‘μ΄ λ™μΌν•œ λ‹€λ₯Έ μ½”λ“œλ‘œ λŒ€μ²΄ν•  μˆ˜λ„ 있고 λ³€μˆ˜λͺ…μ˜ μΆ©λŒμ„ ν”Όν•˜κΈ° μœ„ν•΄ λ³€μˆ˜λͺ…을 λ³€κ²½ν•  μˆ˜λ„ μžˆλŠ”λ°, μ΄λŠ” ν•¨μˆ˜λ₯Ό μΈλΌμΈν™”ν•˜λŠ” ν–‰μœ„λ‚˜ λ°˜λŒ€λ‘œ ν‘œν˜„μ‹μ„ ν•¨μˆ˜λ‘œ λ¦¬νŒ©ν† λ§ν•˜λŠ” ν–‰μœ„λ‘œ 생각할 μˆ˜λ„ μžˆλ‹€. ν•œλ²ˆ ν•­λ“±ν•¨μˆ˜λ₯Ό μ˜ˆμ‹œλ‘œ μ‚΄νŽ΄λ³΄μž.

    id x = x

    μ΄λŸ¬ν•œ ν•¨μˆ˜κ°€ μ‘΄μž¬ν•œλ‹€λ©΄ 이제 μš°λ¦¬λŠ” μ–΄λ–€ ν‘œν˜„μ‹μ—μ„œ id yλ₯Ό λ³΄μ•˜μ„ λ•Œ, 이 μ½”λ“œλ₯Ό y둜 λ°”κΏ”λ³Ό 수 μžˆλ‹€(인라인화). 더 λ‚˜μ•„κ°€μ„œ id (y + 2)와 같이 idκ°€ ν‘œν˜„μ‹μ— μ μš©λ˜μ–΄μžˆλ‹€λ©΄ 이λ₯Ό ν‘œν˜„μ‹ κ·Έ 자체인 (y + 2)둜 λ°”κΏ€ μˆ˜λ„ μžˆλ‹€. λ˜ν•œ 이 μΉ˜ν™˜μ€ μ–‘λ°©ν–₯으둜 적용되기 λ•Œλ¬Έμ— ν‘œν˜„μ‹ eλ₯Ό id e둜 μΉ˜ν™˜ν•  μˆ˜λ„ μžˆλ‹€.(λ¦¬νŒ©ν† λ§)

    ν•¨μˆ˜κ°€ νŒ¨ν„΄ 맀칭에 μ˜ν•΄ μ •μ˜λœ 경우, 각각의 ν•˜μœ„ μ •μ˜λ₯Ό λ…λ¦½μ μœΌλ‘œ μ‚¬μš©ν•  μˆ˜λ„ μžˆλ‹€. 예λ₯Ό λ“€μ–΄ μœ„μ˜ fmap μ •μ˜μ—μ„œ fmap f Nothing을 Nothing으둜 λ°”κΏ€ 수 있으며, λ°˜λŒ€λ‘œλ„ κ°€λŠ₯ν•˜λ‹€. 이제 이 κ°œλ…μ΄ μ‹€μ œλ‘œ μ–΄λ–»κ²Œ μž‘λ™ν•˜λŠ”μ§€λ₯Ό μ‚΄νŽ΄λ³΄μž. λ¨Όμ € ν•­λ“± 보쑴을 μ‚΄νŽ΄λ³΄κ² λ‹€.

    fmap id = id

    μš°λ¦¬λŠ” Nothingκ³Ό JustλΌλŠ” 두 가지 μΌ€μ΄μŠ€λ₯Ό κ³ λ €ν•΄μ•Όν•œλ‹€. λ¨Όμ € 첫 번째 경우λ₯Ό μ‚΄νŽ΄λ³΄κ² λ‹€. μ™Όμͺ½μ˜ 항을 였λ₯Έμͺ½ ν•­μœΌλ‘œ λ³€ν™˜ν•˜κΈ° μœ„ν•΄ Haskell λ¬Έλ²•μ˜ Pseudo Codeλ₯Ό μ‚¬μš©ν•˜κ² λ‹€.

      fmap id Nothing
    = { fmap의 μ •μ˜μ— μ˜ν•˜λ©΄ }
      Nothing
    = { id의 μ •μ˜μ— μ˜ν•˜λ©΄ }
    id Nothing

    κ°€μž₯ λ§ˆμ§€λ§‰ λ‹¨κ³„μ—μ„œλŠ” id의 μ •μ˜λ₯Ό ν™œμš©ν•˜μ—¬ ν‘œν˜„μ‹ Nothing을 id Nothing으둜 λŒ€μ²΄ν–ˆλ‹€. μ‹€μ œλ‘œλŠ” μ–‘ λμ—μ„œ μ΄›λΆˆμ„ νƒœμš°λŠ” 방식과 μœ μ‚¬ν•˜κ²Œ μ΄λŸ¬ν•œ 증λͺ…을 μˆ˜ν–‰ν•΄λ‚˜κ°€λ©°, μ€‘κ°„μ—μ„œ λ™μΌν•œ ν‘œν˜„μ‹μ„ λ§Œλ‚  λ•ŒκΉŒμ§€ μ§„ν–‰ν•˜κ²Œ λœλ‹€. μœ„ μΌ€μ΄μŠ€μ—μ„œλŠ” μ„Έ 번째 라인의 Nothing이 이에 ν•΄λ‹Ήν•œλ‹€.

    두 번째 κ²½μš°λ„ 어렡지 μ•Šλ‹€.

      fmap id (Just x)
    = { fmap의 μ •μ˜μ— μ˜ν•˜λ©΄ }
      Just (id x)
    = { id의 μ •μ˜μ— μ˜ν•˜λ©΄ }
      Just x
    = { id의 μ •μ˜μ— μ˜ν•˜λ©΄ }
      id (Just x)

    이제 ν•­λ“± 보쑴에 λŒ€ν•œ 증λͺ…이 λλ‚¬μœΌλ‹ˆ, fmap이 합성을 λ³΄μ‘΄ν•œλ‹€λŠ” 것도 ν‘œν˜„ν•΄λ³΄μž.

    fmap (g . f) = fmap g . fmap f

    첫 번째 Nothing의 μΌ€μ΄μŠ€μ΄λ‹€.

      fmap (g . f) Nothing
    = { fmap의 μ •μ˜μ— μ˜ν•˜λ©΄ }
      Nothing
    = { fmap의 μ •μ˜μ— μ˜ν•˜λ©΄ }
      fmap g Nothing
    = { fmap의 μ •μ˜μ— μ˜ν•˜λ©΄ }
      fmap g (fmap f Nothing)

    그리고 두 번째 μΌ€μ΄μŠ€μΈ Just에 λŒ€ν•΄μ„œλŠ” μ΄λ ‡κ²Œ ν‘œν˜„ν•œλ‹€.

      fmap (g . f) (Just x)
    = { fmap의 μ •μ˜μ— μ˜ν•˜λ©΄ }
      Just ((g . f) x)
    = { ν•©μ„±μ˜ 결합법칙에 μ˜ν•˜λ©΄ }
      Just (g (f x))
    = { fmap의 μ •μ˜μ— μ˜ν•˜λ©΄ }
      fmap g (Just (f x))
    = { fmap의 μ •μ˜μ— μ˜ν•˜λ©΄ }
      fmap g (fmap f (Just x))
    = { ν•©μ„±μ˜ 결합법칙에 μ˜ν•˜λ©΄ }
      (fmap g . fmap f) (Just x)

    πŸ’‘ μ—­μ£Ό

    μ΄λŸ¬ν•œ μΆ”λ‘  방식이 쑰금 μ–΄μƒ‰ν•˜κ²Œ λŠκ»΄μ§€λŠ” λ…μž 뢄듀도 계싀텐데, 사싀 μœ„ μ˜ˆμ‹œμ— μž‘μ„±λœ λͺ¨λ“  μ½”λ“œλ“€μ€ λͺ¨λ‘ 같은 λ™μž‘μ„ ν•˜λŠ” μ½”λ“œμ΄λ‹€.

    방정식 μΆ”λ‘ μ΄λΌλŠ” 말이 μ–΄λ €μ›Œλ³΄μ—¬μ„œ 그렇지 결ꡭ은 x, x + 0이 λ™ν˜•(Isomorphic)μž„μ„ λ°ν˜€λ‚˜κ°€λŠ” 노가닀와 μœ μ‚¬ν•˜λ‹€.

    const addZero = (x: number) => x + 0;
    const identify = <T>(v: T) => v;
       identify(addZero(x));
    = { identify의 μ •μ˜μ— μ˜ν•˜λ©΄ }
       addZero(x)
    = { addZero의 μ •μ˜μ— μ˜ν•˜λ©΄ }
       x
    = { identify의 μ •μ˜μ— μ˜ν•˜λ©΄ }
       identify(x)

    ν•¨μˆ˜κ°€ μ°Έμ‘° 투λͺ…성을 보μž₯ν•˜λŠ” 이상 ν•¨μˆ˜λ₯Ό ν•¨μˆ˜μ˜ 본문으둜 μΉ˜ν™˜μ΄ κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— μ΄λ ‡κ²Œ λ…Έκ°€λ‹€λ‘œ 정합성을 ν™•μΈν•΄λ‚˜κ°€λŠ” ν–‰μœ„κ°€ κ°€λŠ₯ν•œ 것이닀.

    이와 같은 방정식 좔둠을 μ‚¬μš©ν•  λ•ŒλŠ” 이 μΆ”λ‘  방법이 μ‚¬μ΄λ“œ μ΄νŽ™νŠΈλ₯Ό 가진 C++ μŠ€νƒ€μΌμ˜ ν•¨μˆ˜μ— λŒ€ν•΄μ„œλŠ” λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€λŠ” 점을 잘 μ•Œμ•„μ•Ό ν•œλ‹€. μ•„λž˜ μ½”λ“œλ₯Ό μ‚΄νŽ΄λ³΄μž.

    int square(int x) {
        return x * x;
    }
    int counter() {
        static int c = 0;
        return c++;
    }
    
    double y = square(counter());

    방정식 좔둠을 μ‚¬μš©ν•˜λ©΄ squareλ₯Ό μΈλΌμΈν™”ν•˜μ—¬ μ•„λž˜μ™€ 같은 μ •μ˜λ₯Ό 얻을 수 μžˆλ‹€.

    double y = counter() * counter();

    μ΄λŠ” ν™•μ‹€νžˆ μœ νš¨ν•œ λ³€ν™˜μ΄ μ•„λ‹ˆλ©° 맀번 λ™μΌν•œ κ²°κ³Όλ₯Ό μƒμ„±ν•˜μ§€λ„ μ•Šμ„ 것이닀. κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³  맀크둜λ₯Ό 톡해 squareλ₯Ό κ΅¬ν˜„ν•œ 경우 C++ μ»΄νŒŒμΌλŸ¬λŠ” 방정식 좔둠을 μ‹œλ„ν•  것이고, κ·Έ κ²°κ³ΌλŠ” μ°Έν˜Ήν•  것이닀.

    7.1.3 Optional

    λ¬Όλ‘  Haskell을 μ‚¬μš©ν•˜μ—¬ νŽ‘ν„°λ₯Ό μ‰½κ²Œ μ •μ˜ν•  수 μžˆκΈ°λŠ” ν•˜μ§€λ§Œ, 사싀 κΌ­ Haskell이 μ•„λ‹ˆλ”λΌλ„ μ œλ„€λ¦­ ν”„λ‘œκ·Έλž˜λ°κ³Ό κ³ μ°¨ν•¨μˆ˜λ₯Ό μ§€μ›ν•˜λŠ” μ–΄λ–€ μ–Έμ–΄λ“  νŽ‘ν„°λ₯Ό μ •μ˜ν•  수 μžˆλ‹€. C++의 Maybe에 ν•΄λ‹Ήν•˜λŠ” ν…œν”Œλ¦Ώ νƒ€μž… optional을 μƒκ°ν•΄λ³΄μž. μ•„λž˜λŠ” optional κ΅¬ν˜„μ— λŒ€ν•œ μŠ€μΌ€μΉ˜μ΄λ‹€. (μ‹€μ œ κ΅¬ν˜„μ€ μΈμˆ˜κ°€ 전달될 수 μžˆλŠ” μ—¬λŸ¬κ°€μ§€ 방법, Deep Copy/Shallow Copy와 같은 Copy Semantics, C++ 특유의 μžμ› 관리 문제 λ“± 을 κ³ λ €ν•΄μ•Όν•˜κΈ° λ•Œλ¬Έμ— 훨씬 더 λ³΅μž‘ν•˜λ‹€.)

    template<class T>
    class optional {
        bool _isValid; // the tag
    		T _v;
    public:
        optional()    : _isValid(false) {}        // Nothing
        optional(T x) : _isValid(true) , _v(x) {} // Just
        bool isValid() const { return _isValid; }
        T val() const { return _v; } };

    이 ν…œν”Œλ¦Ώμ€ νƒ€μž… Tλ₯Ό μƒˆλ‘œμš΄ νƒ€μž… optional<T>둜 λ§€ν•‘ν•˜μ—¬ νŽ‘ν„°μ˜ μ •μ˜ 쀑 ν•˜λ‚˜μΈ νƒ€μž…μ— λŒ€ν•œ 맀핑을 μ œκ³΅ν•œλ‹€. 이제 ν•¨μˆ˜μ— λŒ€ν•œ λ™μž‘μ„ μ •μ˜ν•΄λ³΄μž.

    template<class A, class B>
    std::function<optional<B>(optional<A>)>
    fmap(std::function<B(A)> f) {
    		return [f](optional<A> opt) {
    				if (!opt.isValid())
    						return optional<B>{};
    				else
    						return optional<B>{ f(opt.val()) };
    		};
    }

    이것은 ν•¨μˆ˜λ₯Ό 인자둜 λ°›κ³  λ‹€μ‹œ ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•˜λŠ” κ³ μ°¨ν•¨μˆ˜μ΄λ‹€. μ»€λ§λ˜μ§€ μ•ŠλŠ” 버전은 λ‹€μŒκ³Ό κ°™λ‹€.

    template<class A, class B>
    optional<B> fmap(std::function<B(A)> f, optional<A> opt) {
    		if (!opt.isValid())
            return optional<B>{};
        else
            return optional<B>{ f(opt.val()) };
    }

    이외에 fmap을 optional의 ν…œν”Œλ¦Ώ λ©”μ„œλ“œλ‘œ λ§Œλ“œλŠ” 선택지도 μžˆλ‹€. 이처럼 λ‹€μ–‘ν•œ 선택지듀은 C++μ—μ„œ νŽ‘ν„° νŒ¨ν„΄μ„ μΆ”μƒν™”ν•˜λŠ” λ‚œμ΄λ„λ₯Ό 더 λ†’νžˆλŠ” 주범이닀. νŽ‘ν„°λ₯Ό 상속할 수 μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€λ‘œ μ •μ˜ν•΄μ•Όν• κΉŒ? νŽ‘ν„°λŠ” 컀링된 ν•¨μˆ˜μΈκ°€ μ•„λ‹ˆλ©΄ μ»€λ§λ˜μ§€ μ•Šμ€ 프리 ν…œν”Œλ¦Ώ ν•¨μˆ˜μΈκ°€? C++ μ»΄νΌμΌλŸ¬λŠ” λˆ„λ½λœ νƒ€μž…μ„ μ˜¬λ°”λ₯΄κ²Œ μΆ”λ‘ ν•  수 μžˆμ„κΉŒ, μ•„λ‹ˆλ©΄ μš°λ¦¬κ°€ 직접 λͺ…μ‹œμ μœΌλ‘œ μ§€μ •ν•΄μ€˜μ•Ό ν•˜λŠ”κ±ΈκΉŒ?

    ν•œλ²ˆ μž…λ ₯ ν•¨μˆ˜ fκ°€ intλ₯Ό λ°›μ•„ bool을 λ°˜ν™˜ν•œλ‹€κ³  μƒκ°ν•΄λ³΄μž. 이 κ²½μš°μ— μ»΄νŒŒμΌλŸ¬λŠ” g의 νƒ€μž…μ„ μ–΄λ–»κ²Œ κ²°μ •ν• κΉŒ?

    auto g = fmap(f);

    특히 fmap을 μ˜€λ²„λ‘œλ”©ν•˜λŠ” μ—¬λŸ¬ 가지 νŽ‘ν„°κ°€ μžˆλŠ” κ²½μš°μ—λŠ” μ–΄λ–»κ²Œ 될까? (곧 μš°λ¦¬λŠ” 더 λ§Žμ€ νŽ‘ν„°λ“€μ„ λ§Œλ‚˜κ²Œ 될 것이닀)

    7.1.4 νƒ€μž… 클래슀(Typeclasses)

    κ·Έλ ‡λ‹€λ©΄ Haskell이 νŽ‘ν„°λ₯Ό μΆ”μƒν™”ν•˜λŠ” 방법은 λ¬΄μ—‡μΌκΉŒ? 이λ₯Ό μœ„ν•΄μ„œ Haskell은 νƒ€μž… 클래슀 λ§€μ»€λ‹ˆμ¦˜μ„ μ‚¬μš©ν•œλ‹€. νƒ€μž… ν΄λž˜μŠ€λŠ” 곡톡 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ§€μ›ν•˜λŠ” νƒ€μž…μ˜ 집합을 μ •μ˜ν•œλ‹€. 예λ₯Ό λ“€μ–΄, 동등성을 μ§€μ›ν•˜λŠ” κ°μ²΄λ“€μ˜ ν΄λž˜μŠ€λŠ” μ•„λž˜μ™€ 같이 μ •μ˜λœλ‹€.

    class Eq a where
        (==) :: a -> a -> Bool

    이 μ •μ˜λŠ” νƒ€μž… aκ°€ Eq 클래슀의 멀버인 경우, νƒ€μž… aκ°€ 두 인자λ₯Ό λ°›μ•„ Bool을 λ°˜ν™˜ν•˜λŠ” == μ—°μ‚°μžλ₯Ό μ§€μ›ν•œλ‹€λŠ” 것을 λ‚˜νƒ€λ‚Έλ‹€.

    λ§Œμ•½ Haskellμ—κ²Œ νŠΉμ • νƒ€μž…μ΄ Eq νƒ€μž… 클래슀의 μΈμŠ€ν„΄μŠ€λΌλŠ” 것을 μ•Œλ €μ£Όκ³  μ‹Άλ‹€λ©΄, λ¨Όμ € 이 클래슀의 μΈμŠ€ν„΄μŠ€λ‘œ μ„ μ–Έν•˜κ³  ==의 κ΅¬ν˜„μ„ μ œκ³΅ν•΄μ€˜μ•Ό ν•œλ‹€. 예λ₯Ό λ“€μ–΄ 두 개의 Float둜 이루어진 κ³± νƒ€μž…μΈ 2D Point νƒ€μž…μ˜ μ •μ˜κ°€ μ£Όμ–΄μ‘Œλ‹€κ³  μƒκ°ν•΄λ³΄μž.

    data Point = Pt Float Float

    μš°λ¦¬λŠ” 각 점의 동등성을 μ΄λ ‡κ²Œ μ •μ˜ν•΄λ³Ό 수 μžˆλ‹€.

    instance Eq Point where
    		(Pt x y) == (Pt x' y') = x == x' && y == y'

    μ—¬κΈ°μ„œλŠ” 두 개의 νŒ¨ν„΄ (Pt x y)와 (Pt x’ y’)μ—κ²Œ μ€‘μœ„ μ—°μ‚°μž ==λ₯Ό μ‚¬μš©ν•΄λ³΄μ•˜λ‹€. ν•¨μˆ˜μ˜ 본문은 단일 λ“±ν˜Έλ₯Ό 보면 이해가 쉽닀. 이처럼 Pointκ°€ Eq의 μΈμŠ€ν„΄μŠ€λ‘œ μ„ μ–Έλœλ‹€λ©΄ 이제 μ§μ ‘μ μœΌλ‘œ μ—°μ‚°μžλ₯Ό 톡해 각 점의 동등성을 비ꡐ할 수 μžˆλ‹€.

    Haskellμ—μ„œλŠ” C++μ΄λ‚˜ Javaμ™€λŠ” λ‹€λ₯΄κ²Œ Pointλ₯Ό μ •μ˜ν•  λ•Œ, 이 νƒ€μž…μ΄ 직접 Eq클래슀의 λ©€λ²„λΌλŠ” 것을 λͺ…μ‹œν•  ν•„μš”λŠ” μ—†μœΌλ©°, μ΄λŠ” μΆ”ν›„ μ‚¬μš©μžκ°€ 직접 μž‘μ„±ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œμ—μ„œ μž‘μ„±ν•  수 μžˆλ‹€. μ΄λŸ¬ν•œ νƒ€μž… ν΄λž˜μŠ€λŠ” Haskellμ—μ„œ ν•¨μˆ˜λ‚˜ μ—°μ‚°μžλ₯Ό μ˜€λ²„λ‘œλ”©ν•  수 μžˆλŠ” μœ μΌν•œ 방법이닀.

    λ‹€μ–‘ν•œ νŽ‘ν„°λ“€μ—μ„œ fmap의 λ™μž‘μ„ μ˜€λ²„λ‘œλ”©ν•˜μ—¬ ν™œμš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” 이와 같은 νƒ€μž… 클래슀 기법이 ν•„μš”ν•˜λ‹€. κ·ΈλŸ¬λ‚˜ ν•œ 가지 λ³΅μž‘ν•œ 점이 μžˆλ‹€. νŽ‘ν„°λŠ” νƒ€μž…μœΌλ‘œ μ •μ˜λ˜λŠ” 것이 μ•„λ‹ˆλΌ νƒ€μž…λ“€μ˜ 맀핑인 νƒ€μž… μƒμ„±μžλ‘œ μ •μ˜λœλ‹€. 즉, νŽ‘ν„°λ₯Ό μ œλŒ€λ‘œ μ •μ˜ν•˜κΈ° μœ„ν•΄μ„œλŠ” Eq의 경우처럼 νƒ€μž…λ“€μ˜ 집합이 μ•„λ‹Œ νƒ€μž… μƒμ„±μžλ“€μ˜ μ§‘ν•©μœΌλ‘œ μ •μ˜λœ νƒ€μž… ν΄λž˜μŠ€κ°€ ν•„μš”ν•˜λ‹€λŠ” 것이닀. λ‹€ν–‰νžˆλ„ Haskell의 νƒ€μž… ν΄λž˜μŠ€λŠ” νƒ€μž… 뿐만 μ•„λ‹ˆλΌ νƒ€μž… μƒμ„±μž λ˜ν•œ 잘 지원해쀀닀. 그럼 이제 Functor 클래슀λ₯Ό ν•œλ²ˆ μ •μ˜ν•΄λ³΄μž.

    class Functor f where
        fmap :: (a -> b) -> f a -> f b

    이 μ½”λ“œλŠ” fκ°€ Functor인 경우, μ§€μ •λœ νƒ€μž… μ‹œκ·Έλ‹ˆμ²˜λ₯Ό 가진 ν•¨μˆ˜ fmap을 가지고 μžˆλ‹€λŠ” 사싀을 λͺ…μ‹œν•˜κ³  μžˆλ‹€. μ—¬κΈ°μ„œ μ†Œλ¬Έμž fλŠ” νƒ€μž… λ³€μˆ˜μ΄λ©°, νƒ€μž… λ³€μˆ˜ fmap이 λ°›κ³  μžˆλŠ” aλ‚˜ b와 λ™μΌν•œ κ°œλ…μ΄λ‹€.

    μ»΄νŒŒμΌλŸ¬λŠ” f aλ‚˜ f b와 같이 이 νƒ€μž… λ³€μˆ˜κ°€ λ‹€λ₯Έ νƒ€μž…μ„ μ²˜λ¦¬ν•˜κ³  μžˆλ‹€λŠ” 사싀을 μ°Έκ³ ν•˜μ—¬ fκ°€ νƒ€μž…μ΄ μ•„λ‹Œ νƒ€μž… μƒμ„±μžλΌλŠ” 사싀을 μΆ”λ‘ ν•  수 μžˆλ‹€. λ”°λΌμ„œ Functor의 μΈμŠ€ν„΄μŠ€λ₯Ό μ„ μ–Έν•  λ•ŒλŠ” Maybe와 같은 νƒ€μž… μƒμ„±μžλ₯Ό μ§€μ •ν•΄μ€˜μ•Ό ν•œλ‹€.

    instance Functor Maybe where
        fmap _ Nothing = Nothing
        fmap f (Just x) = Just (f x)

    사싀 μš°λ¦¬κ°€ μ •μ˜ν•œ Functor 클래슀, 그리고 Maybe와 같이 κ°„λ‹¨ν•œ 데이터 νƒ€μž…μ— λŒ€ν•œ μΈμŠ€ν„΄μŠ€ μ •μ˜λ“€μ€ 이미 ν‘œμ€€ Prelude에 ν¬ν•¨λ˜μ–΄μžˆλ‹€.

    7.1.5 C++μ—μ„œμ˜ νŽ‘ν„°(Functor in C++)

    κ·Έλ ‡λ‹€λ©΄ C++μ—μ„œλ„ 이와 λ™μΌν•œ μ ‘κ·Ό 방식을 μ‹œλ„ν•΄λ³Ό 수 μžˆμ„κΉŒ? νƒ€μž… μƒμ„±μžλŠ” optionalκ³Ό 같은 ν…œν”Œλ¦Ώ ν΄λž˜μŠ€μ— ν•΄λ‹Ήν•˜λ―€λ‘œ, fmap을 ν…œν”Œλ¦Ώ ν…œν”Œλ¦Ώ λ§€κ°œλ³€μˆ˜ F둜 λ§€κ°œν™”ν•΄λ³Ό 수 μžˆμ„ 것이닀.

    template<template<class> F, class A, class B>
    F<B> fmap(std::function<B(A)>, F<A>);

    이제 이 ν…œν”Œλ¦Ώμ„ λ‹€λ₯Έ νŽ‘ν„°λ“€μ— λŒ€ν•΄μ„œ νŠΉμˆ˜ν™”(Specialize)ν•˜λ©΄ 쒋을 것 κ°™λ‹€. ν•˜μ§€λ§Œ μŠ¬ν”„κ²Œλ„ C++μ—μ„œλŠ” ν…œν”Œλ¦Ώ ν•¨μˆ˜μ˜ λΆ€λΆ„ νŠΉμˆ˜ν™”λ₯Ό κΈˆμ§€ν•˜κ³  있기 λ•Œλ¬Έμ— μ•„λž˜μ²˜λŸΌ μž‘μ„±ν•  μˆ˜κ°€ μ—†λ‹€.

    template<class A, class B>
    optional<B> fmap<optional>(std::function<B(A)> f, optional<A> opt)

    λŒ€μ‹  μš°λ¦¬λŠ” ν•¨μˆ˜ μ˜€λ²„λ‘œλ”©μ„ μ‚¬μš©ν•  것이닀. μ΄λ ‡κ²Œ 되면 μš°λ¦¬λŠ” λ‹€μ‹œ μ»€λ§λ˜μ§€ μ•Šμ€ νŽ‘ν„°μ˜ μ›λž˜ μ •μ˜λ‘œ λŒμ•„κ°€κ²Œ λœλ‹€.

    template<class A, class B>
    optional<B> fmap(std::function<B(A)> f, optional<A> opt) {
        if (!opt.isValid())
            return optional<B>{};
        else
            return optional<B>{ f(opt.val()) };
    }

    이제 fmap의 두 번째 μΈμžκ°€ μ˜€λ²„λ‘œλ“œλ₯Ό μ„ νƒν•˜κΈ° λ•Œλ¬Έμ— 이 μ •μ˜ μžμ²΄κ°€ μž‘λ™ν•˜κΈ°λŠ” ν•˜μ§€λ§Œ, μ΄λŸ¬ν•œ 방법은 fmap의 일반적인 μ •μ˜μ—μ„œλŠ” 많이 λ²—μ–΄λ‚˜μžˆλ‹€.

    7.1.6 List νŽ‘ν„°

    ν”„λ‘œκ·Έλž˜λ°μ—μ„œ νŽ‘ν„°κ°€ μ–΄λ–€ 역할을 ν•˜λŠ”μ§€μ— λŒ€ν•΄ 감을 작기 μœ„ν•΄μ„œλŠ” 쑰금 더 λ§Žμ€ 예제λ₯Ό μ‚΄νŽ΄λ΄μ•Όν•  것 κ°™λ‹€. λ‹€λ₯Έ νƒ€μž…μœΌλ‘œ λ§€κ°œν™”λœ μ–΄λ–€ νƒ€μž…μ΄λ“  λͺ¨λ‘ νŽ‘ν„°μ˜ 후보라고 λ³Ό 수 μžˆλ‹€. μ œλ„€λ¦­ μ»¨ν…Œλ‹ˆμ–΄λŠ” μ €μž₯된 μš”μ†Œμ˜ νƒ€μž…μ— μ˜ν•΄ λ§€κ°œν™”λ˜λ―€λ‘œ, 맀우 κ°„λ‹¨ν•œ μ»¨ν…Œμ΄λ„ˆμΈ Listλ₯Ό ν•œλ²ˆ μ‚΄νŽ΄λ³΄μž.

    πŸ’‘ μ—­μ£Ό

    μ œλ„€λ¦­ μ»¨ν…Œμ΄λ„ˆλŠ” μ—¬λŸ¬ νƒ€μž…μ˜ 데이터λ₯Ό μ €μž₯ν•  수 μžˆλŠ” 데이터 μ»¨ν…Œμ΄λ„ˆμ΄λ‹€. 즉, νƒ€μž… νŒŒλΌλ―Έν„°λ₯Ό λ°›μŒμœΌλ‘œμ¨ λ™μΌν•œ ꡬ쑰λ₯Ό μœ μ§€ν•˜λ©΄μ„œλ„ μ„œλ‘œ λ‹€λ₯Έ μœ ν˜•μ˜ 데이터λ₯Ό μ €μž₯ν•  수 μžˆλŠ” 것이닀. TypeScriptμ—μ„œλŠ” Array<T>와 같은 νƒ€μž…μ΄ μΌμ’…μ˜ μ œλ„€λ¦­ μ»¨ν…Œμ΄λ„ˆλΌκ³  λ³Ό 수 μžˆλ‹€.

    data List a = Nil | Cons a (List a)

    μ—¬κΈ° ListλΌλŠ” νƒ€μž… μƒμ„±μžκ°€ μžˆλ‹€. 이 νƒ€μž… μƒμ„±μžλŠ” μž„μ˜μ˜ νƒ€μž… aλ₯Ό νƒ€μž… List a둜 λ§€ν•‘ν•œλ‹€. μ΄λ•Œ Listκ°€ νŽ‘ν„°λΌλŠ” 것을 보이기 μœ„ν•΄μ„œλŠ” ν•¨μˆ˜ a β†’ bκ°€ μ£Όμ–΄μ‘Œμ„ λ•Œ ν•¨μˆ˜ List a β†’ List b둜 λ‚˜μ•„κ°€λŠ” λ¦¬ν”„νŒ… ν•¨μˆ˜λ₯Ό μ •μ˜ν•΄μ•Ό ν•œλ‹€.

    fmap :: (a -> b) -> (List a -> List b)

    List a에 μž‘μš©ν•˜λŠ” ν•¨μˆ˜λŠ” 두 리슀트 μƒμ„±μžμΈ Nilκ³Ό Cons 두 가지 경우λ₯Ό κ³ λ €ν•΄μ•Όν•œλ‹€. Nil의 κ²½μš°μ—λŠ” μ–΄μ°¨ν”Ό 빈 λ¦¬μŠ€νŠΈμ— λŒ€ν•΄μ„œ ν•  수 μžˆλŠ”κ²Œ μ—†μœΌλ‹ˆ, κ·Έλƒ₯ Nil을 λ°˜ν™˜ν•΄μ£Όλ©΄ λ˜λ―€λ‘œ κ°„λ‹¨ν•˜κ²Œ μ²˜λ¦¬κ°€ κ°€λŠ₯ν•˜λ‹€.

    κ·ΈλŸ¬λ‚˜ Cons μΌ€μ΄μŠ€μ˜ κ²½μš°μ—λŠ” μ•½κ°„ κΉŒλ‹€λ‘­λ‹€. μ™œλƒν•˜λ©΄ μž¬κ·€λ₯Ό ν¬ν•¨ν•΄μ•Όν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. 일단 μž μ‹œ 생각을 λ©ˆμΆ”κ³  μš°λ¦¬κ°€ μ •ν™•νžˆ μ–΄λ–€ 것을 ν•˜λ €κ³  ν•˜λŠ” 것인지 λ‹€μ‹œ μ‚΄νŽ΄λ³΄μž. fmap의 인자둜 a의 리슀트, 그리고 aλ₯Ό b둜 λ³€ν™˜ν•˜λŠ” ν•¨μˆ˜ fκ°€ μ£Όμ–΄μ‘Œκ³ , 이제 이것듀을 가지고 b의 리슀트λ₯Ό μƒμ„±ν•΄μ•Όν•œλ‹€.

    리슀트의 각 μš”μ†Œλ₯Ό aμ—μ„œ b둜 λ³€ν™˜ν•˜λŠ” 것은 λ‹Ήμ—°ν•˜κ²Œλ„ fλ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€. λ‹€λ§Œ λΉ„μ–΄μžˆμ§€ μ•Šμ€ λ¦¬μŠ€νŠΈλŠ” ν•˜λ‚˜μ˜ μ›μ†Œκ°€ μ•„λ‹Œ Cons둜 ν‘œν˜„λ˜λŠ” ν—€λ“œ(Head)와 λ‚˜λ¨Έμ§€ 꼬리(Tail)둜 ꡬ성될텐데, μ–΄λ–»κ²Œ λͺ¨λ“  μ›μ†Œμ— fλ₯Ό μ μš©ν•΄μ•Ό ν•˜λŠ”κ±ΈκΉŒ?

    μš°λ¦¬λŠ” fλ₯Ό ν—€λ“œμ— λ¨Όμ € μ μš©ν•˜κ³ , λ¦¬ν”„νŒ…ν•œ(fmapν•œ) fλ₯Ό λ‚˜λ¨Έμ§€ 꼬리에 μ μš©ν•  것이닀. μ΄λŠ” λ¦¬ν”„νŒ…λœ fλ₯Ό 또 λ‹€μ‹œ λ¦¬ν”„νŒ…ν•œ f둜 μ •μ˜ν•˜κ³  있기 λ•Œλ¬Έμ— μž¬κ·€μ μΈ μ •μ˜κ°€ λœλ‹€.

    fmap f (Cons x t) = Cons (f x) (fmap f t)

    πŸ’‘ μ—­μ£Ό

    Haksell에 μ΅μˆ™ν•˜μ§€ μ•Šμ€ λ…μžλ“€μ„ μœ„ν•΄ 쑰금 더 읽기 μ‰¬μš΄ λ³€μˆ˜λͺ…μœΌλ‘œ 바꿔보면 λ‹€μŒκ³Ό κ°™λ‹€.

    fmap μ›μ†Œμ—μ μš©ν• fn (Cons ν—€λ“œμ›μ†Œ λ‚˜λ¨Έμ§€ν…ŒμΌ) = Cons (μ›μ†Œμ—μ μš©ν• fn ν—€λ“œμ›μ†Œ) (fmap μ›μ†Œμ—μ μš©ν• fn λ‚˜λ¨Έμ§€ν…ŒμΌ)

    이λ₯Ό TypeScript둜 λ‹€μ‹œ μž‘μ„±ν•΄λ³΄μžλ©΄ λŒ€λž΅ 이런 λŠλ‚Œμ΄λ‹€.

    // List νŽ‘ν„° μΈν„°νŽ˜μ΄μŠ€
    interface List<T> {
      fmap<U>(fn: (value: T) => U): List<U>;
    }
    
    class Cons<T> implements List<T> {
      constructor(public head: T, public tail: List<T>) {}
    
      fmap<U>(fn: (value: T) => U): List<U> {
        // ν—€λ“œμ— fnλ₯Ό μ μš©ν•΄μ„œ Tλ₯Ό U둜 λ³€κ²½
        // 이후 꼬리(List)κ°€ 가진 fmap을 ν˜ΈμΆœν•˜μ—¬ 같은 ν–‰μœ„λ₯Ό μž¬κ·€μ μœΌλ‘œ λ°˜λ³΅ν•œλ‹€.
        return new Cons<U>(fn(this.head), this.tail.fmap(fn));
      }
    }

    Haksell은 Cons (f x)λ‚˜ (fmap f t)와 같은 νŒ¨ν„΄λ§€μΉ­μ„ 톡해 ν—€λ“œμ— μ μš©ν•  λ™μž‘κ³Ό 꼬리에 μ μš©ν•  λ™μž‘μ„ κ΅¬λΆ„ν•˜μ§€λ§Œ, TypeScriptλŠ” 그런 νŒ¨λŸ¬λ‹€μž„μ„ 가진 μ–Έμ–΄κ°€ μ•„λ‹ˆκΈ° λ•Œλ¬Έμ— 직접 thisλ₯Ό 톡해 μ›ν•˜λŠ” λ™μž‘μ„ ν˜ΈμΆœν•˜κ³  μžˆλ‹€.

    μš°ν•­μ˜ fmap f ꡬ문은 μ •μ˜ν•˜λ €λŠ” λ¦¬μŠ€νŠΈλ³΄λ‹€ 더 짧은 리슀트, 즉, 꼬리에 μ μš©λœλ‹€. κ²°κ΅­ 점점 더 짧은 리슀트둜 μž¬κ·€ν•˜λ‹€κ°€ κ²°κ΅­ μ΅œμ’…μ μœΌλ‘œλŠ” 빈 리슀트인 Nil에 λ„λ‹¬ν•˜κ²Œ 될 것이닀.

    μ•žμ„œ μ •μ˜ν•œλŒ€λ‘œ fmap fκ°€ Nil에 적용되면 μž¬κ·€κ°€ μ’…λ£Œλœλ‹€. μ΅œμ’… κ²°κ³Όλ₯Ό μ–»μœΌλ €λ©΄ μƒˆλ‘œμš΄ ν—€λ“œ (f x)와 μƒˆλ‘œμš΄ 꼬리 (fmap f t)λ₯Ό Cons μƒμ„±μžλ₯Ό 톡해 κ²°ν•©ν•œλ‹€. μ§€κΈˆκΉŒμ§€ μ΄μ•ΌκΈ°ν•œ λͺ¨λ“  λ‚΄μš©μ„ ν•˜λ‚˜λ‘œ 합쳐보면 μ•„λž˜μ™€ 같이 List νŽ‘ν„°μ˜ μΈμŠ€ν„΄μŠ€ 선언이 λœλ‹€.

    instance Functor List where
        fmap _ Nil = Nil
        fmap f (Cons x t) = Cons (f x) (fmap f t)

    λ§Œμ•½ μ—¬λŸ¬λΆ„μ΄ C++에 μ΅μˆ™ν•˜λ‹€λ©΄, 일반적인 C++ μ»¨ν…Œμ΄λ„ˆμΈ std::vectorλ₯Ό ν•œλ²ˆ 생각해보면 λœλ‹€. std::vector에 λŒ€ν•œ fmap의 κ΅¬ν˜„μ€ λ‹¨μˆœνžˆ std::transform의 얇은 μΊ‘μŠν™”μ— λΆˆκ³Όν•˜λ‹€.

    template<class A, class B>
    std::vector<B> fmap(std::function<B(A)> f, std::vector<A> v) {
        std::vector<B> w;
        std::transform( std::begin(v)
                      , std::end(v)
                      , std::back_inserter(w)
                      , f);
    		return w;
    }

    이λ₯Ό μ‚¬μš©ν•˜λ©΄ 숫자 μ‹œν€€μŠ€μΈ μ›μ†Œλ₯Ό μ œκ³±ν•˜λŠ” λ“±μ˜ ν–‰μœ„κ°€ κ°€λŠ₯ν•˜λ‹€.

    std::vector<int> v{ 1, 2, 3, 4 };
    auto w = fmap([](int i) { return i*i; }, v);
    std::copy( std::begin(w)
    					, std::end(w)
    					, std::ostream_iterator(std::cout, ", "));

    λŒ€λΆ€λΆ„μ˜ C++ μ»¨ν…Œμ΄λ„ˆλŠ” std::transform에 전달할 수 μžˆλŠ” μ΄ν„°λ ˆμ΄ν„°λ₯Ό κ΅¬ν˜„ν•¨μœΌλ‘œμ¨ νŽ‘ν„°κ°€ 되며, μ΄λŠ” fmap의 μ›μ‹œμ μΈ 버전과도 κ°™λ‹€. κ·ΈλŸ¬λ‚˜ μ•ˆνƒ€κΉκ²Œλ„ μ΄ν„°λ ˆμ΄ν„°μ™€ μž„μ‹œ 개체(Temporaries)의 ν˜Όλž€μŠ€λŸ¬μ›€μœΌλ‘œ 인해 νŽ‘ν„°μ˜ λ‹¨μˆœν•¨μ΄ 상당 λΆ€λΆ„ 사라져버린닀(μœ„μ˜ fmap κ΅¬ν˜„μ„ μ°Έμ‘°ν•΄λ³΄μž). ν•˜μ§€λ§Œ μƒˆλ‘­κ²Œ μ œμ•ˆλœ range λΌμ΄λΈŒλŸ¬λ¦¬λŠ” νŠΉμ •ν•œ λ²”μœ„ λ‚΄μ—μ„œ νŽ‘ν„°μ μΈ μ„±μ§ˆμ„ 더 λͺ…ν™•ν•˜κ²Œ ν‘œν˜„ν•΄μ£Όκ³  μžˆλ‹€.

    7.1.7 Reader νŽ‘ν„°

    μ—¬κΈ°κΉŒμ§€ λ΄€λ‹€λ©΄ 이제 νŽ‘ν„°κ°€ μ–΄λ–€ μ»¨ν…Œμ΄λ„ˆμ˜ ν•œ μ’…λ₯˜μΈ 것 κ°™λ‹€λŠ” 생각을 κ°€μ§€κ²Œ λ˜μ—ˆμ„ 것이라 μƒκ°ν•œλ‹€. κ·Έλ ‡λ‹€λ©΄ μ΄μ œλŠ” κ·Έ 생각을 깨버리기 μœ„ν•΄ μ§€κΈˆκΉŒμ§€μ™€ 맀우 λ‹€λ₯Έ κ²ƒμ²˜λŸΌ λ³΄μ΄λŠ” 예제λ₯Ό 보여주렀고 ν•œλ‹€. νƒ€μž… aλ₯Ό aλ₯Ό λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λ‘œ λ§€ν•‘ν•˜λŠ” 상황을 μƒκ°ν•΄λ³΄μž.

    아직 ν•¨μˆ˜ νƒ€μž…μ— λŒ€ν•œ 깊이 μžˆλŠ” 이야기λ₯Ό λ‚˜λˆ„μ§€λŠ” μ•Šμ•˜μ§€λ§Œ, 사싀 ν”„λ‘œκ·Έλž˜λ¨ΈλΌλ©΄ ν•¨μˆ˜μ— λŒ€ν•œ 기본적인 이해 μ •λ„λŠ” 가지고 μžˆλ‹€. Haskellμ—μ„œ ν•¨μˆ˜ νƒ€μž…μ€ 두 개의 νƒ€μž…κ³Ό ν™”μ‚΄ν‘œ νƒ€μž… μƒμ„±μž(->)λ₯Ό μ‚¬μš©ν•˜μ—¬ κ΅¬μ„±λœλ‹€. 이 μƒμ„±μžλŠ” 인수 νƒ€μž…, 그리고 κ²°κ³Ό νƒ€μž…μ΄λΌλŠ” 두 가지 νƒ€μž… μ‚¬μ΄μ˜ μ€‘μœ„ ν‘œν˜„μœΌλ‘œ λ“±μž₯ν•œλ‹€. κΈ°λ³Έμ μœΌλ‘œλŠ” a -> b의 ν˜•νƒœμ΄μ§€λ§Œ κ΄„ν˜Έλ₯Ό μ‚¬μš©ν•˜λ©΄ μ „μœ„ ν‘œν˜„μœΌλ‘œλ„ μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ‹€.

    (->) a b

    μ •κ·œ ν•¨μˆ˜μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ ν•˜λ‚˜ μ΄μƒμ˜ 인수λ₯Ό 가진 νƒ€μž… ν•¨μˆ˜λŠ” λΆ€λΆ„μ μš©μ΄ κ°€λŠ₯ν•˜λ‹€. λ”°λΌμ„œ μ•„λž˜ μ˜ˆμ‹œμ™€ 같이 ν™”μ‚΄ν‘œμ— ν•˜λ‚˜μ˜ νƒ€μž… 인수만 μ œκ³΅ν•œλ‹€λ©΄ κ²°κ³Ό νƒ€μž…μ„ μ˜λ―Έν•˜λŠ” λ‹€λ₯Έ μΈμˆ˜κ°€ λ“€μ–΄μ˜€λŠ” 것을 기닀릴 수 μžˆλ‹€λŠ” 것이닀.

    (->) a

    이것이 λ°”λ‘œ μœ„ ν‘œν˜„μ΄ νƒ€μž… μƒμ„±μžμΈ μ΄μœ μ΄λ‹€. a -> b λ₯Ό μ™„μ „ν•œ νƒ€μž…μœΌλ‘œ λ§Œλ“€κΈ° μœ„ν•΄μ„œλŠ” νƒ€μž… bκΉŒμ§€ μ œκ³΅λ˜μ–΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. 즉, μœ„ μ˜ˆμ‹œλŠ” νƒ€μž… aλ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ μ‚¬μš©ν•˜λŠ” νƒ€μž… μƒμ„±μž 집합을 μ •μ˜ν•˜κ³  μžˆλ‹€κ³  λ³Ό 수 μžˆλ‹€.

    κ·Έλ ‡λ‹€λ©΄ 이제 이 μΌ€μ΄μŠ€κ°€ νŽ‘ν„°κ°€ λ§žλŠ”μ§€ μ‚΄νŽ΄λ³΄λ„λ‘ ν•˜μž. 두 개의 νƒ€μž… λ§€κ°œλ³€μˆ˜λ₯Ό λ‹€λ£¨λŠ” 것은 μ–΄λ €μšΈ 수 μžˆμœΌλ‹ˆ 이름을 쑰금 변경해보겠닀. 이전에 μ •μ˜ν–ˆλ˜ νŽ‘ν„°μ˜ μ •μ˜μ™€ μΌμΉ˜ν•˜λ„λ‘ 인수의 νƒ€μž…μ„ r둜, κ²°κ³Ό νƒ€μž…μ„ a둜 지정해보겠닀.

    즉, 이 νƒ€μž… μƒμ„±μžλŠ” μž„μ˜μ˜ νƒ€μž… aλ₯Ό r -> a νƒ€μž…μœΌλ‘œ λ§€ν•‘ν•˜λŠ” 녀석이닀. 이것이 νŽ‘ν„°κ°€ 되렀면 ν•¨μˆ˜ a -> b, ν•¨μˆ˜ r -> aλ₯Ό 인자둜 λ°›κ³  ν•¨μˆ˜ r -> bλ₯Ό λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λ‘œ λ¦¬ν”„νŒ…ν•΄μ•Ό ν•œλ‹€. 이듀은 각각 νƒ€μž… μƒμ„±μž(->) r이 μž‘μš©ν•˜λŠ” νƒ€μž… a와 bλ₯Ό μ‚¬μš©ν•˜μ—¬ ν˜•μ„±λœ νƒ€μž…μ΄λ‹€.

    이제 이 μΌ€μ΄μŠ€λ₯Ό ν‘œν˜„ν•  수 μžˆλŠ” fmap의 μ •μ˜λ₯Ό μ‚΄νŽ΄λ³΄μž.

    fmap :: (a -> b) -> (r -> a) -> (r -> b)

    μš°λ¦¬λŠ” ν•¨μˆ˜ f::a -> b와 ν•¨μˆ˜ g::r -> aκ°€ μ£Όμ–΄μ‘Œμ„ λ•Œ ν•¨μˆ˜ r -> bλ₯Ό μƒμ„±ν•΄μ•Όν•˜λŠ” 퍼즐을 ν’€μ–΄μ•Ό ν•œλ‹€. 두 ν•¨μˆ˜λ₯Ό ν•©μ„±ν•  수 μžˆλŠ” 방법은 였직 ν•˜λ‚˜ 뿐이며, κ²°κ³Ό λ˜ν•œ μš°λ¦¬κ°€ μ›ν•˜λŠ” λ°©ν–₯κ³Ό μ •ν™•νžˆ μΌμΉ˜ν•œλ‹€. λ”°λΌμ„œ fmap의 κ΅¬ν˜„μ€ μ•„λž˜μ™€ 같을 것이닀.

    instance Functor ((->) r) where
        fmap f g = f . g

    πŸ’‘ μ—­μ£Ό

    g::r β†’ a, f::a β†’ bκ°€ ν•©μ„±λ¨μœΌλ‘œμ¨ r β†’ a β†’ b, 즉 r β†’ bκ°€ μ„±λ¦½λœλ‹€.

    ꡉμž₯히 κ°„κ²°ν•˜μ§€λ§Œ μš°λ¦¬κ°€ μ›ν•˜λŠ” λ™μž‘μ€ μ •ν™•νžˆ κ΅¬ν˜„λ˜μ—ˆλ‹€. λ§Œμ•½ 더 κ°„κ²°ν•œ ν‘œκΈ°λ₯Ό μ„ ν˜Έν•œλ‹€λ©΄, ν•¨μˆ˜ 합성에 λŒ€ν•œ ν‘œν˜„μ„ μ „μœ„ ν‘œκΈ°λ²•μœΌλ‘œ λ°”κΏ”λ³Ό μˆ˜λ„ μžˆλ‹€.

    fmap f g = (.) f g

    그리고 인수λ₯Ό μƒλž΅ν•΄μ„œ fmapκ³Ό ν•¨μˆ˜ 합성을 μ˜λ―Έν•˜λŠ” . μ—°μ‚°μž, 두 ν•¨μˆ˜μ˜ 직접적인 동등성을 ν‘œν˜„ν•  μˆ˜λ„ μžˆλ‹€.

    fmap = (.)

    이처럼 (->) r νƒ€μž… μƒμ„±μžμ™€ fmap κ΅¬ν˜„μ˜ 결합을 Reader νŽ‘ν„°λΌκ³  ν•œλ‹€.

    7.2 μ»¨ν…Œμ΄λ„ˆλ‘œμ¨μ˜ νŽ‘ν„°

    μ§€κΈˆκΉŒμ§€ ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄μ—μ„œ 일반적인 μš©λ„μ˜ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ •μ˜ν•˜λŠ” νŽ‘ν„°μ˜ λͺ‡ 가지 μ˜ˆμ‹œλ₯Ό μ‚΄νŽ΄λ³΄μ•˜λ‹€.

    μš°λ¦¬λŠ” 보톡 ν•¨μˆ˜λ₯Ό μΌμ’…μ˜ 데이터라고 μƒκ°ν•˜μ§€μ•ŠκΈ° λ•Œλ¬Έμ— Reader νŽ‘ν„°κ°™μ€ 녀석듀이 쑰금 어색해보이기도 ν•œλ‹€. κ·ΈλŸ¬λ‚˜ μˆœμˆ˜ν•¨μˆ˜λŠ” λ©”λͺ¨μ΄μ œμ΄μ…˜λ  수 있으며 ν•¨μˆ˜μ˜ 싀행은 μΌμ’…μ˜ ν…Œμ΄λΈ” 쑰회 ν–‰μœ„λ‘œ λ³€ν™˜λ  μˆ˜λ„ μžˆλ‹€. 그리고 ν…Œμ΄λΈ”μ€ 데이터이닀.

    πŸ’‘ μ—­μ£Ό

    ν•¨μˆ˜μ˜ 싀행을 μΌμ’…μ˜ ν…Œμ΄λΈ” 쑰회 ν–‰μœ„λ‘œ λ³€ν™˜ν•  수 μžˆλ‹€λŠ” 말은 μ–΄λ–€ ν•¨μˆ˜κ°€ μˆœμˆ˜ν•˜κ³  λΆˆλ³€μ μ΄λ©° μž…λ ₯에 따라 항상 같은 좜λ ₯을 λ°˜ν™˜ν•œλ‹€λŠ” 가정을 μ „μ œλ‘œ ν•˜λŠ” μ„€λͺ…이닀.

    μœ„μ™€ 같은 μ „μ œκ°€ μ§€μΌœμ§„λ‹€λ©΄ ν•¨μˆ˜μ˜ μ‹€ν–‰ κ²°κ³Όλ₯Ό ν…Œμ΄λΈ”μ— μ €μž₯ν•˜κ³  ν•¨μˆ˜μ˜ μž…λ ₯ 값을 ν‚€λ‘œ μ‚¬μš©ν•˜μ—¬ ν•¨μˆ˜μ˜ μ‹€ν–‰ κ²°κ³Όλ₯Ό β€œκ²€μƒ‰β€ν•  수 μžˆλ‹€.

    μ΄λŸ¬ν•œ λ©”λͺ¨μ΄μ œμ΄μ…˜μ— μ‚¬μš©λ˜λŠ” μžλ£Œκ΅¬μ‘°κ°€ κΌ­ ν…Œμ΄λΈ”μ΄μ–΄μ•Όλ§Œ ν•˜λŠ” 것은 μ•„λ‹ˆμ§€λ§Œ, μž‘κ°€λŠ” μˆœμˆ˜ν•œ ν•¨μˆ˜μ˜ μ΄λŸ¬ν•œ νŠΉμ„±μœΌλ‘œ 인해 μˆœμˆ˜ν•¨μˆ˜μ˜ 싀행이 μΌμ’…μ˜ 데이터 쑰회 ν–‰μœ„κ°€ 될 수 μžˆλ‹€λŠ” 사싀을 κ°•μ‘°ν•˜κ³  μžˆλŠ” 것이닀.

    λ°˜λŒ€λ‘œ Haskell의 게으λ₯Έ 평가(지연평가) λ•Œλ¬Έμ— 전톡적인 μ»¨ν…Œμ΄λ„ˆμΈ λ¦¬μŠ€νŠΈλŠ” ν•¨μˆ˜λ‘œ κ΅¬ν˜„λ  μˆ˜λ„ μžˆλ‹€. 예λ₯Ό λ“€μ–΄ μžμ—°μˆ˜μ˜ λ¬΄ν•œν•œ λ¦¬μŠ€νŠΈλŠ” μ•„λž˜μ™€ 같이 κ°„κ²°ν•˜κ²Œ μ •μ˜ν•  수 μžˆλ‹€.

    nats :: [Integer]
    nats = [1..]

    첫 번째 라인의 λŒ€κ΄„ν˜Έ μŒμ€ Haskell의 λ¦¬μŠ€νŠΈμ— λŒ€ν•œ λ‚΄μž₯ νƒ€μž… μƒμ„±μžμ΄λ‹€. 두 번째 라인의 λŒ€κ΄„ν˜ΈλŠ” 리슀트 λ¦¬ν„°λŸ΄μ„ λ§Œλ“œλŠ” 데 μ‚¬μš©λœλ‹€.

    λ‹Ήμ—°ν•˜κ² μ§€λ§Œ 이런 λ°©μ‹μ˜ λ¬΄ν•œ λ¦¬μŠ€νŠΈλŠ” λ©”λͺ¨λ¦¬μ— μ €μž₯ν•  수 μ—†λ‹€. κ·Έλž˜μ„œ ν•„μš”ν•  λ•Œλ§ˆλ‹€ Integerλ₯Ό μƒμ„±ν•˜λŠ” ν•¨μˆ˜λ‘œ μ΄λŸ¬ν•œ λ™μž‘μ„ κ΅¬ν˜„ν•˜λŠ” 것이닀. Haskell은 사싀상 데이터와 μ½”λ“œ μ‚¬μ΄μ˜ ꡬ뢄을 λͺ…ν™•ν•˜κ²Œ ν•˜μ§€ μ•ŠλŠ”λ‹€. λ¦¬μŠ€νŠΈλŠ” ν•¨μˆ˜λ‘œ 간주될 μˆ˜λ„ 있고, ν•¨μˆ˜λŠ” 인수λ₯Ό 결과에 λ§€ν•‘ν•˜λŠ” ν…Œμ΄λΈ”λ‘œ 간주될 μˆ˜λ„ μžˆλ‹€. 특히 ν›„μžλŠ” ν•¨μˆ˜μ˜ μ •μ˜μ—­μ΄ μœ ν•œν•˜κ³  크기가 크지 μ•Šμ€ κ²½μš°μ—λŠ” κ½€λ‚˜ μ‹€μš©μ μΈ κ°œλ…μ΄λ‹€.

    κ·ΈλŸ¬λ‚˜ strlen을 ν…Œμ΄λΈ” 쑰회둜 κ΅¬ν˜„ν•˜λŠ” 것은 ν˜„μ‹€μ μ΄μ§€ μ•Šλ‹€. μ™œλƒν•˜λ©΄ λ¬΄ν•œνžˆ λ§Žμ€ μ„œλ‘œ λ‹€λ₯Έ λ¬Έμžμ—΄λ“€μ΄ μ‘΄μž¬ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. ν”„λ‘œκ·Έλž˜λ¨Έλ‘œμ¨ μš°λ¦¬λŠ” λ¬΄ν•œμ„ μ’‹μ•„ν•˜μ§€ μ•Šκ² μ§€λ§Œ, μΉ΄ν…Œκ³ λ¦¬ μ΄λ‘ μ—μ„œλŠ” λ¬΄ν•œμ„ μ•„μΉ¨μ‹μ‚¬μ²˜λŸΌ μ¦κΈ°λŠ” 방법을 λ°°μ›Œλ³Ό 수 μžˆλ‹€. λͺ¨λ“  λ¬Έμžμ—΄μ˜ μ§‘ν•©μ΄λ‚˜ 우주의 κ³Όκ±°, ν˜„μž¬, 미래의 μƒνƒœ 같이 λ¬΄ν•œν•œ 것듀도 닀뀄볼 수 μžˆλ‹€λŠ” 말이닀.

    κ·Έλž˜μ„œ ν•„μžλŠ” νŽ‘ν„° 객체(μ—”λ„νŽ‘ν„°μ— μ˜ν•΄ μƒμ„±λœ νƒ€μž…μ˜ 객체)λ₯Ό μ–΄λ–€ νƒ€μž…μ„ 가진 κ°’ λ˜λŠ” 값듀을 μΆ”μƒμ μœΌλ‘œ 가지고 μžˆλŠ” λ¬΄μ–Έκ°€λ‘œ μƒκ°ν•˜λŠ” 것을 μΆ”μ²œν•œλ‹€. 물리적으둜 κ·Έ 값을 가지고 μžˆμ§€λŠ” μ•Šμ§€λ§Œ 말이닀.

    πŸ’‘ μ—­μ£Ό

    μ—¬κΈ°μ„œ μž‘κ°€κ°€ νŽ‘ν„°κ°€ νŠΉμ • νƒ€μž…μ˜ 값을 직접 가지고 μžˆλŠ” 것이 μ•„λ‹ˆλΌ μΆ”μƒμ μœΌλ‘œ 가지고 μžˆλ‹€κ³  ν‘œν˜„ν•˜λŠ” μ˜λ―ΈλŠ” λ‹€μŒκ³Ό κ°™λ‹€. TypeScript의 Arrayλ₯Ό 예둜 λ“€μ–΄λ³΄μž.

    const numbers: number[] = [1, 2, 3]; // 숫자 νƒ€μž…μ˜ λ°°μ—΄
    const doubledNumbers = numbers.map(x => x * 2); // [2, 4, 6]

    μ—¬κΈ°μ„œ number[] νƒ€μž…μ˜ 배열은 사싀 β€œλͺ¨λ“  숫자λ₯Ό 가진 λ°°μ—΄β€μœΌλ‘œ μ •μ˜λ˜μ—ˆλ‹€κ³  봐야 ν•œλ‹€. ν•˜μ§€λ§Œ ν˜„μž¬ 이 배열이 μ‹€μ œλ‘œ λͺ¨λ“  숫자λ₯Ό 가지고 μžˆλŠ” 것은 μ•„λ‹ˆλ©°, 단지 mapκ³Ό 같은 λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ λ°°μ—΄ λ‚΄λΆ€μ˜ 값듀을 κ°„μ ‘μ μœΌλ‘œ λ‹€λ£° 수 μžˆλŠ” λ°©λ²•λ§Œμ„ μ œκ³΅ν•˜κ³  μžˆλ‹€.

    μ΄λŸ¬ν•œ 방법듀을 톡해 이 배열은 이둠상 λͺ¨λ“  숫자λ₯Ό κ°€μ§ˆ 수 μžˆκ² μ§€λ§Œ, μ‹€μ œλ‘œ 이 배열이 λͺ¨λ“  숫자λ₯Ό 가지고 μžˆλŠ” 것은 μ•„λ‹ˆκΈ°μ— β€œμΆ”μƒμ μœΌλ‘œ 가지고 μžˆλ‹€β€λΌλŠ” ν‘œν˜„μ„ μ‚¬μš©ν•œ 것이닀.

    C++μ—μ„œμ˜ μ˜ˆμ‹œλ₯Ό 보자면 std::futureκ°€ μžˆλ‹€. μ–Έμ  κ°€λŠ” 값을 κ°€μ§€κ²Œ λ ν…Œμ§€λ§Œ λ°˜λ“œμ‹œ κ·Έλ ‡λ‹€λŠ” 보μž₯은 μ—†μœΌλ©°, Future λ‚΄λΆ€μ˜ 값에 μ ‘κ·Όν•˜λ €λ©΄ λ‹€λ₯Έ μŠ€λ ˆλ“œμ˜ 싀행이 μ™„λ£Œλ  λ•ŒκΉŒμ§€ κΈ°λ‹€λ €μ•Όν•  μˆ˜λ„ μžˆλ‹€.

    또 λ‹€λ₯Έ μ˜ˆμ‹œλ‘œλŠ” Haskell의 IO 객체가 μžˆλ‹€. 이 κ°μ²΄λŠ” μ‚¬μš©μžμ˜ μž…λ ₯을 λ°›κ±°λ‚˜ β€œHello World!”가 λͺ¨λ‹ˆν„°μ— ν‘œμ‹œλœ 우주의 μƒνƒœλ₯Ό 포함할 μˆ˜λ„ μžˆλ‹€.

    μ΄λŸ¬ν•œ 해석듀에 λ”°λ₯΄λ©΄ νŽ‘ν„° κ°μ²΄λŠ” λ§€κ°œλ³€μˆ˜ν™”λœ νƒ€μž…μ˜ 값을 ν¬ν•¨ν•˜κ±°λ‚˜ 그런 값을 μƒμ„±ν•˜λŠ” 방법을 ν¬ν•¨ν•˜κ³  μžˆλŠ” λ¬΄μ–Έκ°€λ‘œ 바라볼 수 μžˆλ‹€. νŽ‘ν„° λ‚΄λΆ€μ˜ 값에 μ ‘κ·Όν•˜λŠ” λ™μž‘μ€ μ™„μ „νžˆ 선택사항이며 νŽ‘ν„°μ˜ λ²”μœ„μ— κΌ­ ν¬ν•¨λ˜μ–΄μ•Ό ν•˜λŠ” λ™μž‘λ„ μ•„λ‹ˆλ‹€. μš°λ¦¬κ°€ 관심을 κ°€μ Έμ•Όν•˜λŠ” 뢀뢄은 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄μ„œ κ·Έ 값을 μ‘°μž‘ν•  수 μžˆλ‹€λŠ” 것 뿐이닀.

    λ§Œμ•½ κ·Έ 값에 μ ‘κ·Όν•  수 μžˆλ‹€λ©΄ κ·Έ μ‘°μž‘μ— λŒ€ν•œ κ²°κ³Όλ₯Ό λ³Ό 수 μžˆμ–΄μ•Ό ν•œλ‹€. κ·ΈλŸ¬λ‚˜ μ ‘κ·Όν•  수 μ—†λ‹€λ©΄ μš°λ¦¬κ°€ 였직 μ‹ κ²½μ¨μ•Όν•˜λŠ” 것은 κ·Έ μ‘°μž‘λ“€μ΄ μ˜¬λ°”λ₯΄κ²Œ ν•©μ„±λ˜μ—ˆλŠ”μ§€, 그리고 ν•­λ“± ν•¨μˆ˜λ₯Ό ν†΅ν•œ μ‘°μž‘μ€ 아무것도 바꾸지 μ•ŠλŠ”λ‹€λŠ” 사싀 뿐이닀. νŽ‘ν„° 객체 λ‚΄λΆ€μ˜ 값에 μ ‘κ·Όν•˜λŠ” 것이 κ·Έλ ‡κ²Œ μ€‘μš”ν•œ 일이 μ•„λ‹ˆλΌλŠ” 것을 보여주기 μœ„ν•΄ ν•œ 가지 μ˜ˆμ‹œλ₯Ό 보여주겠닀. μ—¬κΈ° 인자 aλ₯Ό μ™„μ „νžˆ λ¬΄μ‹œν•˜λŠ” νƒ€μž… μƒμ„±μžκ°€ μžˆλ‹€.

    data Const c a = Const c

    Const νƒ€μž… μƒμ„±μžλŠ” c와 a 두 개의 νƒ€μž…μ„ λ°›λŠ”λ‹€. ν™”μ‚΄ν‘œ μƒμ„±μž λ•Œμ™€ λ§ˆμ°¬κ°€μ§€λ‘œ 이λ₯Ό λΆ€λΆ„μ μœΌλ‘œ μ μš©ν•΄μ„œ νŽ‘ν„°λ₯Ό λ§Œλ“€μ–΄λ³Ό 것이닀. Const라고 ν•˜λŠ” 데이터 μƒμ„±μžλŠ” c νƒ€μž…μ˜ κ°’ ν•˜λ‚˜λ§Œμ„ μ·¨ν•˜λ©°, μ΄λŠ” a에 λŒ€ν•œ μ˜μ‘΄μ„±μ΄ μ—†λ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€. 이 νƒ€μž… μƒμ„±μžμ— λŒ€ν•œ fmap의 νƒ€μž… μ •μ˜λŠ” μ•„λž˜μ™€ κ°™λ‹€.

    fmap :: (a -> b) -> Const c a -> Const c b

    νŽ‘ν„°κ°€ νƒ€μž… 인수λ₯Ό λ¬΄μ‹œν•˜κ³  있기 λ•Œλ¬Έμ—, fmap의 κ΅¬ν˜„μ—μ„œλ„ ν•¨μˆ˜ 인수λ₯Ό λ¬΄μ‹œν•  수 μžˆλ‹€. ν•¨μˆ˜μ— μ μš©ν•  λŒ€μƒ μžμ²΄κ°€ μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.

    instance Functor (Const c) where
        fmap _ (Const v) = Const v

    였히렀 이런 뢀뢄은 컴파일 μ‹œκ°„μ— λ°œμƒν•˜λŠ” νƒ€μž… μΈμžμ™€ λŸ°νƒ€μž„μ— λ°œμƒν•˜λŠ” κ°’ μ‚¬μ΄μ˜ 더 κ°•ν•œ ꡬ뢄이 μžˆλŠ” C++μ—μ„œ 더 λͺ…ν™•ν•˜κ²Œ λ‚˜νƒ€λ‚  수 μžˆλ‹€.

    template<class C, class A>
    struct Const {
    		Const(C v) : _v(v) {}
    		C _v;
    };

    C++으둜 κ΅¬ν˜„λœ fmap λ˜ν•œ ν•¨μˆ˜μ˜ 인자λ₯Ό λ¬΄μ‹œν•˜κ³  Const 인자λ₯Ό 본래 κ°’κ³Ό ν•¨κ»˜ λ‹€μ‹œ μΊμŠ€νŒ…ν•˜λŠ” 역할을 μˆ˜ν–‰ν•œλ‹€.

    template<class C, class A, class B>
    Const<C, B> fmap(std::function<B(A)> f, Const<C, A> c) {
        return Const<C, B>{c._v};
    }

    λ‹Ήμž₯ μ΄ν•΄ν•˜κΈ°λŠ” μ–΄λ €μšΈ 수 μžˆμ§€λ§Œ 사싀 Const νŽ‘ν„°λŠ” λ§Žμ€ κ΅¬μ‘°λ“€μ—μ„œ ꡉμž₯히 μ€‘μš”ν•œ 역할을 λ‹΄λ‹Ήν•œλ‹€. μΉ΄ν…Œκ³ λ¦¬ μ΄λ‘ μ—μ„œ Const νŽ‘ν„°λŠ” μ•žμ„œ μ–ΈκΈ‰ν–ˆλ˜ λΈ”λž™ν™€μ˜ μ—”λ„νŽ‘ν„°, 즉, Ξ”c\Delta c νŽ‘ν„°μ˜ νŠΉμˆ˜ν•œ κ²½μš°μ΄λ‹€. μΆ”ν›„ μ΄λŸ¬ν•œ κ°œλ…μ— λŒ€ν•΄μ„œλ„ 더 μžμ„Ένžˆ 닀뀄보도둝 ν•˜κ² λ‹€.

    7.3 νŽ‘ν„° ν•©μ„±(Functor Composition)

    νŽ‘ν„°κ°€ 마치 ν•¨μˆ˜μ™€ 같이 합성될 수 μžˆλ‹€λΌλŠ” 것은 κ½€ 직관적인 이해가 κ°€λŠ₯ν•œ 사싀이닀. κ²°κ΅­ 두 νŽ‘ν„°μ˜ ν•©μ„±μ΄λΌλŠ” 것은 νŽ‘ν„°κ°€ νŠΉμ • μΉ΄ν…Œκ³ λ¦¬ λ‚΄μ˜ κ°μ²΄λ‚˜ 사상에 λ§€ν•‘λ˜λŠ” ν–‰μœ„λ“€μ˜ 합성이라고 λ³Ό 수 μžˆλ‹€.

    ν•©μ„±λœ 두 개의 νŽ‘ν„°λ₯Ό κ±°μΉ˜λ”λΌλ„ ν•­λ“± 사상은 κ·ΈλŒ€λ‘œ ν•­λ“± μ‚¬μƒμœΌλ‘œ λ‚¨κ²Œ 되며, μ‚¬μƒλ“€μ˜ ν•©μ„± κ·œμΉ™μ€ μ—¬μ „νžˆ λ™μΌν•œ κ·œμΉ™μ„ κ°€μ§€κ²Œ λœλ‹€. μ΄λŠ” ꡉμž₯히 λ‹¨μˆœν•˜κ³  κ°„λ‹¨ν•œ κ·œμΉ™μ΄λ©°, 특히 μ—”λ„νŽ‘ν„°λ₯Ό ν•©μ„±ν•˜λŠ” 것은 λ”λ”μš± 쉽닀. ν˜Ήμ‹œ maybeTail ν•¨μˆ˜λ₯Ό κΈ°μ–΅ν•˜λŠ”κ°€?

    이 ν•¨μˆ˜λ₯Ό Haskell의 λ‚΄μž₯ 리슀트 κ΅¬ν˜„μ„ μ‚¬μš©ν•΄μ„œ λ‹€μ‹œ μž‘μ„±ν•΄λ³΄κ² λ‹€.

    maybeTail :: [a] -> Maybe [a]
    maybeTail [] = Nothing
    maybeTail (x:xs) = Just xs

    μ§€κΈˆκΉŒμ§€ Nil둜 ν˜ΈμΆœν•˜λ˜ 빈 리슀트 μƒμ„±μžλŠ” 빈 λŒ€κ΄„ν˜Έ 쌍인 []으둜 λŒ€μ²΄λ˜μ—ˆκ³ , Cons μƒμ„±μžλŠ” μ€‘μœ„ μ—°μ‚°μž :으둜 λŒ€μ²΄λ˜μ—ˆλ‹€.

    maybeTail의 κ²°κ³ΌλŠ” 두 νŽ‘ν„° Maybe, []κ°€ a에 μž‘μš©ν•˜λŠ” ν˜•νƒœλ₯Ό μ§€λ‹ˆκ³  μžˆλ‹€. λ§Œμ•½ μ—¬κΈ°μ„œ ν•©μ„±λœ Maybe 리슀트의 μ›μ†Œμ— μ–΄λ–€ ν•¨μˆ˜ fλ₯Ό μ μš©ν•˜λ €κ³  ν•œλ‹€λ©΄ μ–΄λ–»κ²Œ μ ‘κ·Όν•΄μ•Όν• κΉŒ?

    ν•¨μˆ˜κ°€ Maybe 리슀트의 μ›μ†Œμ— λ„λ‹¬ν•˜κΈ° μœ„ν•΄μ„œλŠ” Maybe와 []λΌλŠ” 두 겹의 νŽ‘ν„°λ₯Ό λš«μ–΄μ•Ό ν•œλ‹€. κ°€μž₯ λ¨Όμ € μ™ΈλΆ€μ˜ Maybeλ₯Ό 뚫기 μœ„ν•΄ fmap을 μ‚¬μš©ν•  수 μžˆλ‹€. κ·ΈλŸ¬λ‚˜ ν•¨μˆ˜ fλŠ” λ¦¬μŠ€νŠΈμ— λŒ€ν•΄μ„œλŠ” μž‘λ™ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μ΄λŒ€λ‘œ 이 ν•¨μˆ˜λ₯Ό Maybe의 μ•ˆμͺ½μœΌλ‘œ 보낼 μˆ˜λŠ” μ—†λ‹€. μš°λ¦¬λŠ” λ‚΄λΆ€ λ¦¬μŠ€νŠΈμ—μ„œ μž‘λ™ν•  (fmap f)λ₯Ό 보내야 ν•œλ‹€. ν•œλ²ˆ Maybe 리슀트의 μ •μˆ˜ μ›μ†Œλ“€μ„ μ œκ³±ν•˜λŠ” μ˜ˆμ‹œλ₯Ό 보도둝 ν•˜μž.

    square x = x * x
    
    mis :: Maybe [Int]
    mis = Just [1, 2, 3]
    
    mis2 = fmap (fmap square) mis

    λ¨Όμ € μ»΄νŒŒμΌλŸ¬λŠ” νƒ€μž…μ„ λΆ„μ„ν•œ ν›„, μ™ΈλΆ€μ˜ fmap에 λŒ€ν•΄μ„œλŠ” Maybe μΈμŠ€ν„΄μŠ€μ˜ κ΅¬ν˜„μ„ μ‚¬μš©ν•˜κ³ , λ‚΄λΆ€μ˜ fmap에 λŒ€ν•΄μ„œλŠ” 리슀트 νŽ‘ν„°μ˜ κ΅¬ν˜„μ„ μ‚¬μš©ν•΄μ•Όν•œλ‹€λŠ” 것을 μΆ”λ‘ ν•΄λ‚Ό 것이닀. λ¬Όλ‘  μ§€κΈˆ μ‹œμ μ—μ„œ μœ„ μ½”λ“œκ°€ μ•„λž˜μ™€ 같은 μ½”λ“œλ‘œ λ‹€μ‹œ μž‘μ„±λ  수 μžˆλ‹€λŠ” 사싀이 λ°”λ‘œ 이해가 λ˜μ§€λŠ” μ•Šμ„ μˆ˜λ„ μžˆλ‹€.

    mis2 = (fmap . fmap) square mis

    κ·ΈλŸ¬λ‚˜ fmap은 ν•˜λ‚˜μ˜ μΈμˆ˜μ— λŒ€ν•œ ν•¨μˆ˜λ‘œ 간주될 수 μžˆλ‹€λŠ” 것을 κΈ°μ–΅ν•΄μ•Όν•œλ‹€.

    fmap :: (a -> b) -> (f a -> f b)

    이 경우 (fmap . fmap)μ•ˆμ˜ 두 번째 fmap은 λ‹€μŒκ³Ό 같은 νƒ€μž…μ˜ 인수λ₯Ό μ·¨ν•œλ‹€.

    square :: Int -> Int

    그리고 λ‹€μŒκ³Ό 같은 νƒ€μž…μ˜ ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•  것이닀.

    [Int] -> [Int]

    이후 첫 번째 fmap은 이 ν•¨μˆ˜λ₯Ό μ·¨ν•˜κ³  λ‹€μŒκ³Ό 같은 νƒ€μž…μ˜ ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•œλ‹€.

    Maybe [Int] -> Maybe [Int]

    그리고 λΉ„λ‘œμ†Œ 이 ν•¨μˆ˜κ°€ mis에 μ μš©λœλ‹€. λ”°λΌμ„œ 두 νŽ‘ν„°μ˜ 합성은 ν•΄λ‹Ή fmapλ“€μ˜ 합성인 νŽ‘ν„°λΌκ³  λ³Ό 수 μžˆλ‹€.

    λ‹€μ‹œ μΉ΄ν…Œκ³ λ¦¬ 이둠으둜 λŒμ•„κ°€λ³΄μž. λŒ€μƒλ“€μ˜ 맀핑이 결합법칙을 λ§Œμ‘±ν•˜λ©°, μ‚¬μƒλ“€μ˜ 맀핑도 결합법칙을 λ§Œμ‘±ν•˜λ‹ˆ νŽ‘ν„°μ˜ ν•©μ„± λ˜ν•œ 결합법칙을 λ§Œμ‘±ν•œλ‹€λŠ” 사싀은 λͺ…ν™•ν•˜λ‹€. 그리고 λͺ¨λ“  μΉ΄ν…Œκ³ λ¦¬μ—λŠ” λ‹Ήμ—°νžˆ ν•­λ“± νŽ‘ν„° λ˜ν•œ μ‘΄μž¬ν•œλ‹€. ν•­λ“± νŽ‘ν„°λŠ” λͺ¨λ“  λŒ€μƒκ³Ό 사상이 μžκΈ°μžμ‹ μ—κ²Œ λ§€ν•‘λ˜λŠ” νŽ‘ν„°μ΄λ‹€.

    즉, νŽ‘ν„°λŠ” 마치 μ–΄λ– ν•œ μΉ΄ν…Œκ³ λ¦¬μ˜ 사상듀과 λ™μΌν•œ νŠΉμ„±μ„ κ°€μ§€κ²Œ λœλ‹€. κ·Έλ ‡λ‹€λ©΄ κ·Έ μΉ΄ν…Œκ³ λ¦¬λž€ λ¬΄μ—‡μΌκΉŒ?

    λ°”λ‘œ λŒ€μƒμ΄ μΉ΄ν…Œκ³ λ¦¬μ΄κ³ , 사상이 νŽ‘ν„°μΈ μΉ΄ν…Œκ³ λ¦¬μ΄λ‹€. 즉, μΉ΄ν…Œκ³ λ¦¬λ“€μ˜ μΉ΄ν…Œκ³ λ¦¬λΌκ³  ν•  수 μžˆλ‹€. κ·ΈλŸ¬λ‚˜ λͺ¨λ“  μΉ΄ν…Œκ³ λ¦¬λ“€μ˜ μΉ΄ν…Œκ³ λ¦¬ λ˜ν•œ μΉ΄ν…Œκ³ λ¦¬μ΄λ‹ˆ, 자기 μžμ‹ μ„ 자기 μžμ‹  μ•ˆμ— ν¬ν•¨ν•΄μ•Όν•˜λŠ” λͺ¨μˆœμ— λΉ μ§€κ²Œ λœλ‹€.

    λͺ¨λ“  β€œμž‘μ€β€ μΉ΄ν…Œκ³ λ¦¬μ˜ μΉ΄ν…Œκ³ λ¦¬μΈ Catμ΄λΌλŠ” κ°œλ…μ΄ μžˆλ‹€. μž‘μ€ μΉ΄ν…Œκ³ λ¦¬λŠ” λŒ€μƒλ“€μ΄ 집합을 ν˜•μ„±ν•˜λŠ” μΉ΄ν…Œκ³ λ¦¬λ₯Ό 가리킀며, 심지어 λ¬΄ν•œν•˜μ—¬ μ…€ 수 μ—†λŠ” 집합도 β€œμž‘μ€β€ μΉ΄ν…Œκ³ λ¦¬λ‘œ κ°„μ£Όλœλ‹€. ν•„μžλŠ” 이런 κ°œλ…λ“€μ΄ λ‹€μ–‘ν•œ 좔상화 μˆ˜μ€€μ—μ„œ λ™μΌν•œ ꡬ쑰가 λ°˜λ³΅λ˜λŠ” 것을 ν‘œν˜„ν•  수 μžˆλ‹€λŠ” 것에 맀우 놀라웠닀. 심지어 λ‚˜μ€‘μ—λŠ” νŽ‘ν„°λ“€μ΄ μΉ΄ν…Œκ³ λ¦¬λ₯Ό ν˜•μ„±ν•˜λŠ” μΌ€μ΄μŠ€λ„ 보게 될 것이닀.

    Evan Moon

    🐒 거뢁이처럼 μ‚΄μž

    κ°œλ°œμ„ μž˜ν•˜κΈ° μœ„ν•΄μ„œκ°€ μ•„λ‹Œ κ°œλ°œμ„ 즐기기 μœ„ν•΄ λ…Έλ ₯ν•˜λŠ” κ°œλ°œμžμž…λ‹ˆλ‹€. μ‚¬μ†Œν•œ 생각 정리뢀터 νŠœν† λ¦¬μ–Ό, μ‚½μ§ˆκΈ° 정도λ₯Ό 주둜 끄적이고 μžˆμŠ΅λ‹ˆλ‹€.