ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๊ณต๋ถ€ํ•˜๋‹ค๋ณด๋ฉด ๋ฒฝ์„ ํ•œ๋ฒˆ ๋งˆ์ฃผํ•œ๋‹ค. Functor์™€ Monad๊ฐ€ ๊ทธ๊ฒƒ์ด๋‹ค. ์ด๊ฑธ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ ์œ„ํ‚คํ”ผ๋””์•„์—์„œ ์ง‘ํ•ฉ๋ก ?, ๋ฒ”์ฃผ๋ก ? ์ด๋Ÿฐ ๊ฑธ ์ฝ์—ˆ๋˜ ์ ์ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™์€๋ฐ ์—ฌ์ „ํžˆ ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค. ์ด๋ฒˆ์—๋Š” ์™€๋‹ฟ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•œ๋‹ค.

Functor

  • optional์˜ map

    • ๊ฐ’์ด ์žˆ์œผ๋ฉด ์‹คํ–‰, ์—†์œผ๋ฉด ๋ง์•„
  • Collection์˜ map

    • ๊ฐ’์„ ํ•˜๋‚˜์”ฉ ๊ฐ€์ง€๊ณ ์™€์„œ ๋งคํ•‘ํ•ด๋ผ
  • Dictionary์˜ map

    • ๊ฐ ๋”•์…”๋„ˆ๋ฆฌ์˜ key, value๋ฅผ tuple๋กœ ๋“ค๊ณ ์™€์„œ ๋งคํ•‘ํ•ด๋ผ
  • function์˜ map

    • Composition ํ•ด๋ผ
  • map์„ ์ด์šฉํ•œ transform์„ ์ง€์›ํ•˜๋Š”, value๋ฅผ ๊ฐ€์ง€๋Š” context

  • Context + value + transform(map) = Functor

    • context
      • ์–ด๋–ค value๊ฐ€ ์ฒ˜ํ•ด์žˆ๋Š” ์ƒํƒœ
      • Collection, Optional ๋“ฑ ๋‹ค๋ฅธ ๊ฐ’์„ ๊ฐ€์ง€๋Š” Container
      • ํฌํ•จํ•˜๋Š” value๊ฐ€ generic์œผ๋กœ ํ‘œํ˜„๋˜์–ด์•ผ ํ•จ
    • value
      • Context์— ๋„ฃ์–ด์ง€๋Š” ์‹ค์ œ ๋‚ด์šฉ
      • Context๊ฐ€ generic์œผ๋กœ ํ‘œํ˜„๋˜๊ธฐ ๋Œ€๋ฌธ์—, ์–ด๋–ค type์˜ value๋„ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
    • Transform
      • ์–ด๋–ค ๊ฐ’ T๋ฅผ U๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” function
      • T์™€ U๋Š” ๊ฐ™์€ Type์ด์–ด๋„ ๊ดœ์ฐฎ์Œ
      • ์‹คํŒจํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์—†๋Š” ๊ฒฝ์šฐ๋ฅผ ๋งํ•จ
        • ์‹คํŒจํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ๊ฒฝ์šฐ๋Š” flatMap์ด ๋‚˜์Œ
  • Context์— ์‹ธ์—ฌ์žˆ๋Š” Value์— function์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ

    • Optional

      • Box๋กœ ์ดํ•ดํ•˜๊ธฐ
      enum Optional<Wrapped> {
          case some(Wrapped)
          case none
      }
      • ์ด๋Ÿฐ Optional(Context)์—๋‹ค๊ฐ€ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” guard, if์™€ ๊ฐ™์ด Optional Unwrapping์„ ํ•ด์ฃผ์–ด์•ผ ํ•จ
    • Map์„ ์‚ฌ์šฉํ•˜๋ฉด, Optional Unwrapping์ด ์—†์ด ๊ฐ™์€ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜์ด ๊ฐ€๋Šฅํ•จ

      Optional(2).map { (v: Int) -> Int in
          return v + 3
      }
      • 5๋ฅผ ๋ฆฌํ„ดํ–ˆ์œผ๋‚˜, map์˜ ๊ฒฐ๊ณผ๋Š” Optional(5)
    • ๋™์ž‘ ์ดํ•ดํ•˜๊ธฐ

      • Value๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ
        • Optional(2) โ†’ Context๋กœ ๋ถ€ํ„ฐ Unwrapping โ†’ 2 โ†’ ํ•จ์ˆ˜ ์ ์šฉ(+3) โ†’ 5 โ†’ Context์— Rewrapping โ†’ Optional(5)
      • Value๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ
        • nil โ†’ ๊ฐ’ ์—†์Œ ํ™•์ธ โ†’ ํ•จ์ˆ˜ ์ ์šฉํ•˜์ง€ ์•Š์Œ โ†’ ๊ทธ๋Œ€๋กœ ๋ฆฌํ„ด(nil)
  • Chaining

    • Optional Chaining
      • let result = a?.b?.c?.d?.e
    • Map Chaining
      • let result = a.map { $0 }.map { $0 }.map { $0 }.map { $0 }
      • ์ด๊ฒŒ ๊ฐ€๋Šฅํ•œ ์ด์œ ๋Š” Context๊ฐ€ ์•ˆ๋ฐ”๋€Œ๊ธฐ ๋•Œ๋ฌธ(Box)
      • ์˜๋ฏธ
        • Value๊ฐ€ ์—†๋Š” ์˜ˆ์™ธ ์ƒํ™ฉ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์ฃผ logic์—์„œ ์ œ๊ฑฐํ•˜์—ฌ ๋”ฐ๋กœ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
        • Step์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ณผ ์ˆ˜ ์žˆ์Œ
        • ํ‰์†Œ์— Code๋ฅผ ์ž‘์€ ๋‹จ์œ„์˜ ํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌํ•ด์•ผ ์ ์šฉ ๊ฐ€๋Šฅ
        • ๊ฐ„๊ฒฐํ•ด์ง€๊ณ  ๊ฐ€๋…์„ฑ์ด ๋†’์•„์ง
  • Function์— map ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ?

    • ์ฆ‰, Function์—ญ์‹œ Context์ธ๊ฐ€?

    • Function์— map์„ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์€ Function์„ composition ํ•œ๋‹ค๋Š” ์˜๋ฏธ

      func map<A, B, C>(f: @escaping (B) -> C, g: @escaping: (A) -> B) -> (A) -> C {
          return { x in
              f(g(x))
          }
      }
      • A โ†’ B ํ•จ์ˆ˜์™€ B โ†’ C ํ•จ์ˆ˜๋ฅผ ํ•ฉ์„ฑํ•ด์„œ A โ†’ C ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋ผ
  • Result Functor

    enum Result<T> {
        case value(T)
        case error(Error)
     
        func map<U>(_ transform: (T) -> U) -> Result<U> {
            switch self {
                case .value(let value):
                  let transformed = transform(value)
                  return Result<U>.value(transformed)
                case .error(let error):
                  return Result<U>.error(error)  
            }
        }
    }
    • Optional์€ ๊ฐ’์ด ์žˆ๊ฑฐ๋‚˜ ์—†๊ฑฐ๋‚˜
    • ํ•˜์ง€๋งŒ ์‹คํŒจํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์•Œ์•„๋ณด๊ธฐ ์œ„ํ•ด์„œ Optional๋งŒ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ •๋ณด๋ฅผ ๋งŽ์ด ์ œ๊ณตํ•˜์ง€ ๋ชปํ•œ๋‹ค.
    • ๊ทธ๋Ÿฐ๋ฐ case๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์ธ ๊ฒฝ์šฐ, ์‹ค์ œ ์‚ฌ์šฉํ•  ๋•Œ, switch ๋ฌธ์„ ํ†ตํ•ด์„œ ๊ณ„์† ํŒ๋‹จํ•ด์ค˜์•ผ ํ•œ๋‹ค. ์ง€์ €๋ถ„ ํ•˜๋‹ค.
      • switch ๋ฌธ์„ ์‹ค์ œ ๋กœ์ง์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์ง€ ์•Š์•˜์Œ
    • ๊ทธ๋ž˜์„œ ๋‚ด๋ถ€์— map ํ•จ์ˆ˜๋ฅผ ๋„์ž…ํ•ด์„œ(๋ฆฌํ„ด๊ฐ’์„ ๋ณด๋ฉด context๊ฐ€ ์œ ์ง€๋˜๊ธฐ ๋•Œ๋ฌธ์— map์ด ๋งž๋‹ค) ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ•จ
    • ํ™œ์šฉ
      func f(string: String) -> Result<Int> {
          guard let integer=  Int(string) else {
              return Result<Int>.error(MyError.notANumber)
          }
          return Result<Int>.value(integer)
      }
       
      func transform10(value: Int) -> Int {
          return value * 10
      }
       
      let result = f(string: "1234").map(transform10(value:))

Monad

  • flatMap์„ ์ด์šฉํ•œ transform์„ ์ง€์›ํ•˜๋Š” value๋ฅผ ๊ฐ€์ง€๋Š” context
  • Context + value + flatMap transform = Monad
    • map์€ Context๋ฅผ ์œ ์ง€ํ•จ
    • transform์˜ ๊ฒฐ๊ณผ์— ์ƒ๊ด€์—†์ด ๋ฐ•์Šค์•ˆ์— ๋‹ค์‹œ ๋„ฃ์Œ(Context)
    • ๊ทธ๋ž˜์„œ ์œ„์— ์„ค๋ช…์‹œ, ๋ชจ๋‘ ๋ณ€ํ™˜์ด ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•˜๋ผ ํ–ˆ๋˜ ๊ฒƒ์ž„
      • ์‚ฌ์ƒ์ด ๊ฐ™์€ ์ง‘ํ•ฉ๋‚ด์—์„œ ์™„์ „ํ•˜๊ฒŒ ์ด๋ค„์ง€๋Š” ๊ฒƒ์„ ๋ฐ”๋žŒ
    • ๋งŒ์•ฝ ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ (transform์˜ ๊ฒฐ๊ณผ๊ฐ€ nil์ด๋ผ๋ฉด)์—๋Š” transform์˜ ๊ฒฐ๊ณผ์ธ nil์„ ๋‹ค์‹œ ์ƒ์ž์— ๋„ฃ๋Š”(Context) ์ƒํ™ฉ์ด ์ดˆ๋ž˜๋จ
      • ์ฆ‰, transform์˜ output์ด Optional์ธ ์ƒํ™ฉ์„ ๋งํ•จ
      • Optional(nil), Optional(Optional(3))
    • ์ด๋Ÿฐ ๊ฒฝ์šฐ๋Š” ๋ณดํ†ต ๋ฐ”๋ผ์ง€ ์•Š๋Š” ์ƒํ™ฉ์ผ ๊ฒƒ์ž„
      • nil, Optional(3)์„ ์›ํ•จ
    • ์ด ๊ฒฝ์šฐ, map์˜ ์—ฐ์‚ฐ์„ ํ•˜๋˜, nil์ธ ๊ฒฝ์šฐ๋ฅผ ์ œ์™ธํ•˜๋Š” flatten์˜ ์—ญํ• ์ด ํ•„์š”ํ•จ
    • ์ด ๋‘ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด flatMap์ž„
  • Monad ํ™œ์šฉ
    • ์„œ๋ฒ„๋กœ ๋ถ€ํ„ฐ ๋ฐ›์€ Data๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค๊ณ  ํ•˜์ž

      • { "age": 3, "name": "kim" }
    • JSON Parser ํ›„, Struct๋กœ ๋ณ€ํ™˜

      struct Person {
          var name: String
          var age: Int
      }
    • ๊ธฐ์กด ์ฝ”๋“œ

      struct Person {
          var name: String
          var age: Int
      }
       
      func convertor(data: Data?) -> Person? {
          guard let unwrappedData = data,
              let json = try? JSONSerialization.jsonObject(with: unwrappedData, options: []),
              let dict = json as? [String: AnyObject],
              let name = dict["name"] as? String,
              let age = dict["age"] as? Int else { return nil }
          
          return Person(name: name, age: age)
      }
    • ๋ณ€๊ฒฝ๋œ ์ฝ”๋“œ

      struct Person {
          var name: String
          var age: Int
          
          init(name: String, age: Int) {
              self.name = name
              self.age = age
          }
          
          init?(dict: [String: AnyObject]) {
              guard let age = dict["age"] as? Int,
                  let name = dict["name"] as? String else {
                      return nil
                  }
              self.init(name: name, age: age)
          }
      }
       
      func parser(data: Data) -> [String: AnyObject]? {
          let json = try? JSONSerialization.jsonObject(with: data, options: [])
          return (json as? [String: AnyObject])
      }
       
      func convertor2(data: Data?) -> Person? {
          return data.flatMap({ parser(data: $0) })
                      .flatMap({ Person(dict: $0) })
      }
       
      func convertor3(data: Data?) -> Person? {
          return data.flatMap(parser(data:))
                      .flatMap(Person.init(dict:))
      }
      • ๋„ˆ๋ฌด๋‚˜ ๊ฐ„๊ฒฐํ•ด์กŒ๋‹ค.
    • ๊ฒฐ๋ก 

      • ์ž‘์—… ์ˆœ์„œ
        • ๋ช…ํ™•ํ•œ ์ž‘์—… ๋‹จ์œ„์˜ function์œผ๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค.
        • Data์˜ Mapping์„ map, flatMap์˜ Chaining์œผ๋กœ ์ฒ˜๋ฆฌ
        • ๊ฒฐ๊ณผ์— ๋Œ€ํ•œ ํŒ๋‹จ์€ ์ตœ์ข…์ ์œผ๋กœ transform๋œ ๋‚ด์šฉ์„ ๋ณธ๋‹ค.
      • ์žฅ์ 
        • ๊ฐ ๋‹จ์œ„ function์ด ๋ช…ํ™•ํ•ด์ง€๊ณ  Testํ•˜๊ธฐ ์šฉ์ดํ•ด์ง
        • ๋ณธ์—ฐ์˜ ๋กœ์ง์ด ๋ช…ํ™•ํ•˜๊ฒŒ ๋“œ๋Ÿฌ๋‚จ
        • ๋ฐ˜๋ณต์ ์œผ๋กœ ๋‚˜์˜จ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ์ฝ”๋“œ๋“ค์ด ์‚ฌ๋ผ์ง