๊ทธ๋ž˜์„œ Monad๋ฅผ ์™œ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?

์™œ ๋ชจ๋‚˜๋“œ๊ฐ€ ์œ ์šฉํ•œ๊ฐ€?

Improve High-Demensional lift

  • Monad๋Š” Functor์˜ ๋ถˆ๋งŒ์กฑ์Šค๋Ÿฌ์šด ๋ถ€๋ถ„์„ ํ•ด์†Œํ•ด์ค€๋‹ค.
  • ์•ž์—์„œ lift2d์— ๋Œ€ํ•ด ๋ฐฐ์› ๋‹ค.
  • lift2d: ((T, U) -> V) -> (F<T>, F<U>) -> F<F<V>>
  • ๋ฌธ์ œ๋Š” ๋ฐ˜ํ™˜ ํƒ€์ž…์— ์ œ๋„ค๋ฆญ์ด ์—ฌ๋Ÿฌ๊ฐœ ๊ฑธ๋ฆฐ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
  • ๊ฐ€๋”์€ ์œ ์šฉํ•˜์ง€๋งŒ ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ๋ถˆํŽธํ•˜๋‹ค.
  • list๊ฐ™์€ ๊ฒƒ์€ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, optional๊ฐ™์€ ๊ฒƒ์€ ๋ณ„ ์“ธ๋ฐ๊ฐ€ ์—†๋‹ค.
  • ์—ฌ๊ธฐ์„œ flat์„ ์“ฐ๋ฉด ๋‹ค์ค‘ ์ œ๋„ค๋ฆญ์„ ์ œ๋„ค๋ฆญ ํ•˜๋‚˜๋กœ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.
  • Functor์™€ Monad ์‚ฌ์ด์—๋Š” Applicative Functor๋ผ๋Š” ๋‹จ๊ณ„๊ฐ€ ํ•˜๋‚˜ ๋” ์กด์žฌํ•˜๊ธด ํ•œ๋‹ค. ๋‚˜์ค‘์— ์•Œ์•„๋ณด์ž.

Enabld to compose Generic-returners

  • ์œ„์˜ ์ด์œ ๋ณด๋‹ค ๋” ์ค‘์š”ํ•œ ์ด์œ ๊ฐ€ ์žˆ๋‹ค.
  • ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋“ค์˜ ํ•ฉ์„ฑ์„ ๊ฐ€๋Šฅ์ผ€ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
  • T -> M<U>, U -> M<V> ๋‘๊ฐœ์˜ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜์ž.
  • ๋ณดํ†ต์€ ์ด ๋‘ ํ•จ์ˆ˜๋ฅผ ํ•ฉ์„ฑํ•  ์ˆ˜ ์—†๋‹ค.
  • ์™œ๋ƒํ•˜๋ฉด U์™€ M<U>๋Š” ๋‹ค๋ฅธ ํƒ€์ž…์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • ๊ทธ๋Ÿฐ๋ฐ Monad๋Š” ์ด๊ฒŒ ๊ฐ€๋Šฅํ•˜๋‹ค. Monad๋Š” T์˜ ์˜๋ฏธ๋ฅผ ์œ ์ง€ํ•œ ์ƒํƒœ๋กœ ํ™•์žฅํ•˜๋Š” ํƒ€์ž…์ด๋‹ˆ๊นŒ.
// Optional
 
func f(_ t: T) -> Optional<U> {
    // ...
}
 
func g(_ u: U) -> Optional<V> {
    // ...
}
 
func gf(_ t: T) -> Optional<V> {
    let opu = f(t)
    switch opu {
        case .none:
            return .none
        case .some(let u):
            return g(u)
    }
}
  • gf์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด๊ฑด ํ•ฉ์„ฑํ•œ ๊ฒฐ๊ณผ๋ฌผ์€ ์•„๋‹ˆ๊ณ , ์ด๋Ÿฐ ์‹์œผ๋กœ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์Œ์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•จ์ด๋‹ค.
  • swift์™€ ๊ฐ™์€ ์–ธ์–ด์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค.
lift(g): Optional<U> -> Optional<Optional<V>>
flat o lift(g): Optional<U> -> Optional<V>
  • lift(g)๋ฅผ ์ ์šฉํ•˜๋ฉด ๊ฒฐ๊ณผ๊ฐ’์ด Optional<Optional<V>>๊ฐ€ ๋‚˜์˜จ๋‹ค.
  • ์—ฌ๊ธฐ์„œ flat์„ ์–ด๋– ํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ฑธ๋ฉด ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ๋‹ค.
  • Monad๋Š” flat์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋‹ˆ ์ด๋Ÿฌํ•œ ์‹์œผ๋กœ ํ™•์žฅํ•œ ์—ฐ์‚ฐ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.
  • ์ง์ ‘ ์ •์˜ํ•  ํ•„์š”๋„ ์—†์ด Monad์˜ ํŠน์„ฑ์„ ๊ฐ€์ง€๊ณ  ํ•ฉ์„ฑํ•˜์—ฌ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
  • ์ด์ œ ๊ฐ ์žก์•˜๊ฒ ์ง€๋งŒ ์ด๊ฑธ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด flatMap์ด๋‹ค.
func f(_ t: T) -> Optional<U> {
    // ...
}
 
func g(_ u: U) -> Optional<V> {
    // ...
}
 
let result = Optional(3)
              .flatMap(f) // Optional<T> -> Optional<U>์˜ ํ•จ์ˆ˜๋กœ ํ™•์žฅ
              .flatMap(g) // Optional<U> -> Optional<V>์˜ ํ•จ์ˆ˜๋กœ ํ™•์žฅ
  • lift์˜ ๋ณ€ํ™˜ํ•จ์ˆ˜๋กœ Optional์ด ๋ฆฌํ„ด๋˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋„ฃ์—ˆ์œผ๋‚˜, ๊ฒฐ๊ณผ๋Š” flatํ•ด์„œ ๋‚˜์™”๋‹ค.
  • ์ด ๋™์ž‘์„ ์—ฎ์–ด์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด flatMap์ด๋‹ค.
  • flat o lift, ๋‘ ํ•จ์ˆ˜๋ฅผ ํ•ฉ์„ฑํ•œ ๊ฒƒ์„ ๊ธฐ๋ณธ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด flatMap์ด๋‹ค.
f: T -> M<U>
g: U -> M<V>

lift(g): M<U> -> M<M<V>>
flat o (lift(g)): M<U> -> M<V> // ์–ด๋– ํ•œ ๋ฐฉ์‹์œผ๋กœ ํ•ฉ์„ฑํ•˜๋ฉด~
(flat o (lift(g))) o f: T -> M<V> // ์–ด๋– ํ•œ ๋ฐฉ์‹์œผ๋กœ ํ•ฉ์„ฑํ•˜๋ฉด~

๊ฒฐ๋ก : flatLift(f) = flat o (lift(f))
  • ์ •ํ™•ํžˆ ์œ„ ๊ณ„์‚ฐ์ด ์–ด๋– ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ๋‹ค ์ค„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•˜์ง€๋ง๊ณ , ์ด๋Ÿฐ ์‹์œผ๋กœ ํ•ฉ์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„๋‘์ž.
  • o์— ๋Œ€์‘๋˜๋Š” ์–ด๋– ํ•œ ํ•ฉ์„ฑ ์—ฐ์‚ฐ์„ ๋‚ด๊ฐ€ ์ž˜ ๊ตฌํ˜„ํ•˜๋ฉด, ์œ„ ์ฒ˜๋Ÿผ gfํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์ง€ ์•Š๊ณ ,
  • ๋ชจ๋‚˜๋“œ์˜ ํŠน์„ฑ์„ ์‚ฌ์šฉํ•ด์„œ ํ•ฉ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

flatLift

์ „์—ญํ•จ์ˆ˜๋กœ ๊ตฌํ˜„

func flat<T>(_ value: Optional<Optional<T>>) -> Optional<T> {
    switch value {
        case .none:
            return .none
        case .some(let wrapped):
            return wrapped
    }
}
 
func lift<T, U>(_ transform: @escaping (T) -> U) -> (Optional<T>) -> Optional<U> {
    return { (input: Optional<T>) -> Optional<U> in
        switch input {
            case .none:
                return .none
            case .some(let wrapped):
                return .some(transform(wrapped))
        }
    }
}
 
func flatLift<T, U>(_ transform: @escaping (T) -> Optional<U>) -> ((Optional<T>) -> Optional<U>) {
    return { (input: Optional<T>) -> Optional<U> in
        flat(
            lift(transform) // Optional<T> -> Optional<Optional<U>>
                (input) // Optional<Optional<U>>
        ) // Optional<U>
    }
}
 
// ๋‹ค์‹œ ์“ฐ๋ฉด,
 
func flatLift<T, U>(_ transform: @escaping (T) -> Optional<U>) -> ((Optional<T>) -> Optional<U>) {
    { input in
        flat(lift(transform)(input))
    }
}

Extension์œผ๋กœ ๊ตฌํ˜„

internal enum Optional<T> {
    case none
    case some(T)
}
 
extension Optional {
 
    internal func flat<T>(_ oot: Optional<Optional<T>>) -> Optional<T> {
        switch oot {
        case .none:
            return .none
        case .some(let ot):
            return ot
        }
    }
 
    internal func lift<T, U>(_ transform: @escaping (T) -> U) -> (Optional<T>) -> Optional<U> {
        { input in
            switch input {
            case .none:
                return .none
            case .some(let wrapped):
                return .some(transform(wrapped))
            }
        }
    }
 
    internal func flatLift<T, U>(_ transform: @escaping (T) -> Optional<U>) -> ((Optional<T>) -> Optional<U>) {
        { input in
            flat(lift(transform)(input))
        }
    }
}

High-Demensional lift with FlatLift

  • ๊ทธ๋Ÿผ lift์ฒ˜๋Ÿผ flatLift์€ ๋‹ค๋ณ€์ˆ˜ํ•จ์ˆ˜์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?
  • ๋‹น์—ฐํžˆ ๋œ๋‹ค.
  • flatLift๊ฐ€ lift์—์„œ๋ณด๋‹ค ๋‹ค๋ณ€์ˆ˜ ํ•จ์ˆ˜์—์„œ ๋ณด๋‹ค ๋” ์œ ์šฉํ•˜๋‹ค.
  • ์ด๋Š” ๋‹น์—ฐํžˆ flatํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
flatLift2d: ((T, U) -> M<V>) -> ((M<T>, M<U>) -> M<V>)
  • lift์˜ ๊ฒฝ์šฐ ์ฐจ์› ํ™•์žฅ์‹œ, ๋ณ€์ˆ˜ ๊ฐœ์ˆ˜์— ๋”ฐ๋ผ ๋ฐ˜ํ™˜ ํƒ€์ž…์˜ ์ œ๋„ค๋ฆญ์ด ์ค‘์ฒฉ๋˜์—ˆ์ง€๋งŒ,
  • flatLift์˜ ๊ฒฝ์šฐ ์ฐจ์› ํ™•์žฅ์‹œ, ๋ณ€์ˆ˜ ๊ฐœ์ˆ˜์— ๋”ฐ๋ผ ๋ฐ˜ํ™˜ ํƒ€์ž…์˜ ์ œ๋„ค๋ฆญ์ด ์ค‘์ฒฉ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ด๋Š” ์—„์ฒญ๋‚˜๊ฒŒ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š”๋ฐ, ๊ทธ ๋•Œ๋ฌธ์— ์ผ๋ถ€ ์–ธ์–ด์—์„œ๋Š” ์ด๋ฅผ ์œ„ํ•œ ์ถ•์•ฝ ํ‘œ๊ธฐ๋ฅผ ์ œ๊ณตํ•  ์ •๋„์ด๋‹ค.
    • do(Haskell), for(Scala)

flatLift2d

  • ์•ž์—์„œ ์ฐจ์› ํ™•์žฅ์œผ๋กœ ๊ณ ์ƒ์„ ๋‹ค ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์–˜๋Š” ์‰ฝ๋‹ค!
func lift<T, U>(_ transform: @escaping (T) -> U) -> (T?) -> U? {
    { input in
        switch input {
        case .none:
            return .none
        case .some(let wrapped):
            return .some(transform(wrapped))
        }
    }
}
 
func flat<T>(_ value: T??) -> T? {
    switch value {
    case .none:
        return .none
    case .some(let wrapped):
        return wrapped
    }
}
 
func flatLift<T, U>(_ transform: @escaping (T) -> U?) -> (T?) -> U? {
    { input in
        flat(lift(transform)(input))
    }
}
 
func flatLift2d<T1, T2, U>(_ transform: @escaping (T1, T2) -> U?) -> (T1?, T2?) -> U? {
    { mt1, mt2 in
        flat(lift { t1 in
            flat(lift { t2 in
                transform(t1, t2)
            }(mt2))
        }(mt1))
    }
}
 
func flatLift2d<T1, T2, U>(_ transform: @escaping (T1, T2) -> U?) -> (T1?, T2?) -> U? {
    { mt1, mt2 in
        flatLift { t1 in
            flatLift { t2 in
                transform(t1, t2)
            }(mt2)
        }(mt1)
    }
}
 
func flatLift3d<T1, T2, T3, U>(_ transform: @escaping (T1, T2, T3) -> U?) -> (T1?, T2?, T3?) -> U? {
    { mt1, mt2, mt3 in
        flatLift { t1 in
            flatLift { t2 in
                flatLift { t3 in
                    transform(t1, t2, t3)
                }(mt3)
            }(mt2)
        }(mt1)
    }
}

flatLift์— ๊ฐ’ ๋„ฃ์–ด๋ณด๊ธฐ

Optional

  • ์ž„์˜์˜ ํ•จ์ˆ˜ f๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜๋˜์—ˆ๋‹ค๊ณ  ํ•˜์ž.
  • f: (T, U) -> Optional<V>
let opt: Optional<T>
let opu: Optional<U>
lift2d(f)(opt, opu)
flatLift2d(f)(opt, opu)

  • ์ž, lift๋ฅผ ๋งŒ์•ฝ์— ๊ฑธ์—ˆ๋‹ค๋ฉด ์œ„๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”์„ ๊ฒƒ์ด๋‹ค.

  • flatLift๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ๋œ๋‹ค.
  • ๋‘˜๋‹ค ๊ฐ’์ด ์žˆ๋Š” ๊ฒฝ์šฐ๋งŒ ์ œ๋Œ€๋กœ ๋–จ์–ด์ง€๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋นˆ ์˜ต์…”๋„์ด ๋‚˜์˜จ๋‹ค.
  • ๋‚˜๋จธ์ง€ ๊ฒฝ์šฐ๋ฅผ ํ•˜๋‚˜์˜ ํƒ€์ž…์œผ๋กœ ์••์ถ•ํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.
  • 3์ฐจ์›์ด๋ฉด ์•„๋งˆ ์ •์œก๋ฉด์ฒด์˜ ํ˜•ํƒœ๋กœ ๋ชจ์–‘์ด ๋‚˜์˜ค๊ณ , ํ•œ ์…€์„ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€๊ฐ€ none์œผ๋กœ ๋–จ์–ด์งˆ ๊ฒƒ์ด๋‹ค.

List

  • ์ž„์˜์˜ ํ•จ์ˆ˜ f๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜๋˜์—ˆ๋‹ค๊ณ  ํ•˜์ž.
  • f: (T, U) -> [V]
let left = [t1, t2, t3]
let right = [u1, u2]
lift2d(f)(left, right)
flatLift2d(f)(left, right)
  • lift2d๋ฅผ ๊ฑธ๋ฉด ์ด๋ ‡๊ฒŒ ๋‚˜์˜จ๋‹ค.
[
  [f(t1, u1), f(t1, u2)],
  [f(t2, u1), f(t2, u2)],
  [f(t3, u1), f(t3, u2)]
]
  • flatLift2d๋ฅผ ๊ฑธ๋ฉด ์ด๋ ‡๊ฒŒ ๋‚˜์˜จ๋‹ค.
[
  f(t1, u1),
  f(t1, u2),
  f(t2, u1),
  f(t2, u2),
  f(t3, u1),
  f(t3, u2)
]

๊ทธ๋ž˜์„œ flatLift๋Š”?

flatLift: `((T1, T2โ€ฆ) โ†’ M) โ†’ ((M, Mโ€ฆ) โ†’ M)๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜

  • ((T1, T2...) -> U)๋กœ ๋ณ€ํ™˜ํ•จ์ˆ˜๊ฐ€ ๋“ค์–ด์˜ค๋ฉด?
    • ๊ทธ๊ฒƒ๋„ ์œ„์˜ ํ˜•ํƒœ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
    • ๋ฆฌํ„ดํ•  ๋•Œ U์— unitํ•จ์ˆ˜ ๊ฑธ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

Reference