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

์™œ ์ฐจ์›ํ™•์žฅ flatLift๋Š” ์ค‘์š”ํ• ๊นŒ?

  • ๋‹ค๋ณ€์ˆ˜๋ฅผ ๋ฐ›์•„ ๋ชจ๋‚˜๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋„ ํ•ฉ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • f1: T1 -> M<U1>,
  • f2: T2 -> M<U2>,
  • g: (U1, U2) -> M<V>
  • ์œ„ ์„ธ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜์ž.
  • ์•ž์—์„œ ํ–ˆ๋˜ lift2d๋ฅผ g์— ์‚ฌ์šฉํ•ด๋ณด์ž. (Monad๋Š” Functor๋‹ˆ๊นŒ)
func lift2d<T, U, V>(_ transform: @escaping (T, U) -> V) -> (F<T>, F<U>) -> F<F<V>> {
    { ft, fu in
        lift { (t: T) -> V? in
            lift { (u: U) -> V in
                transform(t, u)
            }(fu)
        }(ft)
    }
}
 
lift2d(g) // (M<U1>, M<U2>) -> M<M<V>>
  • ๋‹น์—ฐํžˆ ์ด๋ ‡๊ฒŒ ๋‚˜์˜ฌ ๊ฒƒ์ด๋‹ค.
  • ์ด์ œ f1๊ณผ f2๋ฅผ ํ•ฉ์„ฑํ•ด๋ณด์ž.
let t1: T1 = someT1
let t2: T2 = someT2
 
lift2d(g)(f1(t1), f2(t2)) // M<M<V>>
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด f1 ํ•จ์ˆ˜์™€ f2 ํ•จ์ˆ˜๋ฅผ g๋กœ ํ•ฉ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.
  • ๊ทธ๋Ÿฐ๋ฐ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด M<M<V>>๊ฐ€ ๋‚˜์˜จ๋‹ค.
  • ๋ชจ๋‚˜๋“œ์ธ ๊ฒฝ์šฐ ์ด๊ฑธ flatํ•  ์ˆ˜ ์žˆ์–ด, ๋ณดํ†ต ์›ํ•˜๋Š” ๊ฒฐ๊ณผ์ธ M<V>๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
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)
    }
}
 
flatLift2d(g)(f1(t1), f2(t2)) // M<V>
  • ๋ณ€์ˆ˜๊ฐ€ ๋ช‡๊ฐœ์ด๋“  ์ด๋Ÿฐ์‹์œผ๋กœ ํ•ฉ์„ฑํ•œ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์ˆ˜ ์žˆ๋‹ค!

๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋Š”๊ฐ€?

  • ๊ทธ๋ ‡๋‹ค๋ฉด ์œ„์ฒ˜๋Ÿผ ์—ฐ์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ๋ฌด์—‡์ด ์ข‹์€ ๊ฑธ๊นŒ?
  • Functor๋กœ๋Š” ๋‹ค๋ณ€์ˆ˜ ํ•จ์ˆ˜๋ฅผ ํ•ฉ์„ฑํ–ˆ์„ ๋•Œ์˜ ๊ฒฐ๊ณผ๊ฐ€ ํƒ€์ž…์ด ์ค‘์ฒฉ๋˜์–ด ๋‚˜์˜จ๋‹ค.
  • ํ•˜์ง€๋งŒ Monad๋กœ๋Š” ์ด๋ฅผ flatํ•  ์ˆ˜ ์žˆ์–ด ํƒ€์ž…์ด ์ค‘์ฒฉ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ด๋Š” ๊ณง Monad ํ˜•ํƒœ์˜ ๋ฐ˜ํ™˜ ํ•จ์ˆ˜๋กœ ๋ณ€ํ™˜ํ•จ์ˆ˜๋ฅผ ๋‹ค ๋งŒ๋“ ๋‹ค์Œ์— ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•  ์ˆ˜ ์žˆ์Œ์„ ๋œปํ•œ๋‹ค.
  • ์ฆ‰, ๋ณ€ํ™˜์˜ ๊ฒฐ๊ณผ์— โ€œํ™•์žฅ๋œ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง„ ํƒ€์ž…(๋ชจ๋‚˜๋“œ)โ€œ๋กœ ๋ณ€ํ™˜ํ•˜๋„๋ก ๋งŒ๋“ค๊ณ , ์ด๊ฒƒ๋“ค ๋งŒ์œผ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๊ฐ€๋Šฅํ•จ์„ ์‹œ์‚ฌํ•œ๋‹ค.

์–ธ์ œ ์œ ์šฉํ•œ๊ฐ€?

  • ์ด๋ ‡๊ฒŒ๋งŒ ๋งํ•˜๋ฉด ์–ธ์ œ ์œ ์šฉํ•œ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค.
  • ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ๋ผ๋Š” ๊ฒƒ์„ ๋ถ„๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ

  • ์€ํ–‰ ๊ด€๋ จ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž.
  • ๊ฐ๊ธฐ ์„ธ๊ฐœ์˜ ๊ธฐ๋Šฅ์€ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋‚˜ ๋ชจ๋“ˆ๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์„ ๊ฒƒ์ด๋‹ค.
  • ํ•˜์ง€๋งŒ ์ด ์„ธ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š”๋ฐ ์žˆ์–ด, ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ(Cross-cutting concern)๋Š” ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ์—์„œ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์—์„œ ๊ณตํ†ต์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๊ธฐ๋Šฅ ๋˜๋Š” ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ด€์‹ฌ์‚ฌ๋“ค์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์ด๋‚˜ ์ปดํฌ๋„ŒํŠธ์— ๊ฑธ์ณ์„œ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๋ฉฐ, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด๋‚˜ ์ฃผ์š” ๊ธฐ๋Šฅ๊ณผ๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ฃผ์š” ๊ธฐ๋Šฅ๊ณผ ๋ณ„๊ฐœ๋กœ ์กด์žฌํ•˜๋Š” ์ด๋Ÿฌํ•œ ๊ณตํ†ต์ ์ธ ๊ด€์‹ฌ์‚ฌ๋“ค์€ ์ฝ”๋“œ์˜ ์ค‘๋ณต, ๊ฐ€๋…์„ฑ ์ €ํ•˜, ์œ ์ง€๋ณด์ˆ˜ ์–ด๋ ค์›€ ๋“ฑ์„ ์•ผ๊ธฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋กœ๊น…, ์บ์‹ฑ, ๋ณด์•ˆ, ํŠธ๋žœ์žญ์…˜ ๋“ฑ์ด ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ์— ํ•ด๋‹นํ•œ๋‹ค.
  • ๋ชจ๋‚˜๋“œ๊ฐ€ ์ด๋Ÿฐ ๊ฒƒ ๋ถ„๋ฆฌํ•˜๋Š” ์šฉ๋„๋กœ ์ œ๊ฒฉ์ด๋‹ค.

ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ์— ์ ์šฉ

  • ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•˜๋‹ค.
  • ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ์— ํ•ด๋‹นํ•˜๋Š” ๋ชจ๋“  ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ํ˜•์„ ๋ชจ๋‚˜๋“œ ํƒ€์ž…์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋กœ ๋ฐ”๊พธ๋ฉด ๋œ๋‹ค.
  • ๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด ์ด ๋‹ค์Œ ํ•จ์ˆ˜๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ โ€œ๋ฌด์กฐ๊ฑด flatLift๋ฅผ ํ˜ธ์ถœโ€ํ•ด์•ผ ํ•œ๋‹ค.
  • ๋์ด๋‹ค.
func withdraw(_ money: Int?) -> Int? {
    guard let money = money else {
        self.error()
        return nil
    }
    return money
}
 
func deposit(_ money: Int?) -> Int? {
    guard let money = money else {
        self.error()
        return nil
    }
    return money
}
 
func error() -> String {
    return "error"
}
 
let money = Optional(1000)
withdraw(money) // Optional(1000)
  • ์ด๋ ‡๊ฒŒ ๋งŒ๋“ค๋ฉด money๊ฐ€ ๊ฐ’์ด ์—†์„ ๋•Œ ์ฒ˜๋ฆฌ๋ฅผ ํ•จ์ˆ˜ ๋‚ด์—์„œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
  • withdraw, deposit๋ชจ๋‘ ๊ฐ™์€ ๋™์ž‘์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๋ฉด, ์ด๋ฅผ ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค๊ณ  ์ผ์ผํžˆ ํ˜ธ์ถœํ•ด์ค˜์•ผ ํ•œ๋‹ค.
func withdraw(_ money: Int) -> Int? {
    return money
}
 
func deposit(_ money: Int) -> Int? {
    return money
}
 
let result = Optional(1000).flatMap(withdraw) // Optional(1000)
 
switch result {
    case .none:
        error()
    case .some(let money):
        // some action
}
  • Monad๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฐ ๊ณตํ†ต์˜ ์—๋Ÿฌ์ฒ˜๋ฆฌ๋ฅผ ํ•จ์ˆ˜๋ฐ–์œผ๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๋‹ค๋ฅธ ๋ชจ๋“ˆ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋™์ž‘์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋„ ๋ถ„๋ฆฌํ•ด์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

๋ชจ๋‚˜๋“œ๋ฅผ ๋™์ž‘์˜ ํ™•์žฅ ๋„๊ตฌ๋กœ ๋ฐ”๋ผ๋ณธ๋‹ค๋ฉด.

  • ์ง€๊ธˆ๊นŒ์ง€๋Š” ์ด๋Ÿฌํ•œ ํ˜•ํƒœ๋งŒ ๋ฐฐ์› ๋‹ค.
  • ์š”์•ฝํ•˜๋ฉด, ๋ณ€ํ™˜ ํ•จ์ˆ˜๋กœ ๋“ค์–ด์˜ค๋Š” ํ•จ์ˆ˜์˜ ์ž…๋ ฅ ์ธ์ž์˜ ํƒ€์ž…์ด Monad๊ฐ€ ์•„๋‹Œ, ์‹ค์ œ ํƒ€์ž…์œผ๋กœ ๋“ค์–ด์˜ค๋Š” ๊ฒฝ์šฐ๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ๋งŒ ์—ฐ์Šตํ–ˆ๋‹ค.

  • ํ•˜์ง€๋งŒ ์ž…๋ ฅ์œผ๋กœ ๋“ค์–ด์˜ค๋Š” ์ธ์ž์—๋„ Monad๊ฐ€ ์žˆ๋”๋ผ๋„ ๊ฒฐ๊ณผํ•จ์ˆ˜๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.
  • ์œ„์˜ ๋ชจ๋“  ๋ณ€ํ™˜์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ๊ณ ์ฐจ์› Lift๋ฅผ ํ•  ๋•Œ ํ–ˆ์—ˆ๋˜ ๊ฑด๋ฐ, ๋‹น์—ฐํžˆ ๋œ๋‹ค.
func flatLifte1<T1, T2, U>(_ transform: @escaping (T1, T2) -> U) -> (T1?, T2) -> U? {
    { mt1, t2 in
        flatLift { t1 in
            transform(t1, t2)
        }(mt1)
    }
}
  • t1์˜ ๊ฐ’์ด ๋“ค์–ด์˜ฌ ๊ฑฐ์•ผ.
  • ๋“ค์–ด์˜ค๋ฉด ๋ณ€ํ™˜ํ•ด.
  • ๊ทธ๋ฆฌ๊ณ  ์ดํ•จ์ˆ˜๋ฅผ flatLiftํ•ด โ†’ T1? -> U?๋กœ ๋ฐ”๋€œ
  • ๊ทธ ํ•จ์ˆ˜์— ๋Œ€์ž….
func flatLifte2<T1, T2, U>(_ transform: @escaping (T1, T2) -> U) -> (T1, T2?) -> U? {
    { t1, mt2 in
        flatLift { t2 in
            transform(t1, t2)
        }(mt2)
    }
}
 
func flatLifte3<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 flatLifte4<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 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)
    }
}
  • e3, e4, 2d๋Š” ๊ตฌํ˜„์ด ์™„์ „ํžˆ ๊ฐ™์€ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋ชจ๋‚˜๋“œ ํƒ€์ž…๊ฐ„์˜ ํ•จ์ˆ˜๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์—†๋Š” ํ•จ์ˆ˜๊ฐ€ ์—†๋‹ค.

  • ๋‹ค๋งŒ ์ฃผ์˜ํ•  ์ ์ด ์žˆ๋‹ค.
  • ํƒ€์ž…๋งŒ์œผ๋กœ๋ณด๋ฉด ๋‘ ๊ฒฝ๋กœ๋ฅผ ํ†ตํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™์•„๋ณด์ด๋‚˜, ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค.
  • ํž˜์„ ๊ฒฝ๋กœ์ ๋ถ„์‹œ ์–ด๋–ค ๊ฒฝ๋กœ๋ฅผ ์„ ํƒํ•˜๋ƒ์— ๊ด€๊ณ„์—†์ด ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™๋‹ค๋ฉด ๋ณด์กด๋ ฅ(ex: ์ค‘๋ ฅ)์ด๋ผ ํ•˜๊ณ ,
  • ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒƒ์„ ๋น„๋ณด์กด๋ ฅ์ด๋ผ ํ–ˆ์—ˆ๋‹ค.
  • flatLift ์—ฐ์‚ฐ์˜ ๊ฒฝ์šฐ ๋น„๋ณด์กด๋ ฅ๊ณผ ๊ฐ™์€ ํŠน์ง•์„ ๊ฐ–๋Š”๋‹ค ์ดํ•ดํ•˜๋ฉด ๋˜๊ฒ ๋‹ค.

Reference