iOS/Swift

[Swift] 모나드(Monad)

year.number 2022. 8. 15. 15:45

 

모나드

  • 값을 어딘가에 포장하는 개념에 대한 이해에서 출발한다
  • 함수객체(Functor)와 모나드(Monad)는 특정 기능이 아닌 디자인 패턴 혹은 자료구조라고 할 수 있다

 

모나드가 갖춰야하는 조건 3가지

  1. 타입을 인자로 받는 타입
  2. 특정 타입의 값을 포장한 것을 반환하는 함수가 존재
  3. 포장된 값을 변환하여 같은 형태로 포장하는 함수가 존재

 

모나드의 대표적인 예시: 
🔥 옵셔널 - 값이 있을지 없을지 모르는 상태를 박스에 담아두는 것

옵셔널은 
(1) Wrapped 타입을 인자로 받는 제네릭 타입이다
(2) Optional<Int>, init(2) 처럼 다른 타입의 값을 갖는 상태의 컨텍스트를 생성할 수 있다
(3) 옵셔널은 컨테이너와 값을 갖기 때문에 맵 함수를 사용할 수 있다

 

 

모나드를 이해하기 위해 필요한 개념

1. 컨텍스트(Context)

2. 함수객체(Functor)

 


컨텍스트(Context)

스위프트에서 컨텍스트는 콘텐츠를 담은 그 무엇인가(박스)이다

ex)
2라는 숫자를 옵셔널로 둘러싸면 컨텍스트 안에 2라는 콘텐츠가 들어가있는 것이다.
= 컨텍스트는 2라는 값을 가지고 있다

값이 없는 옵셔널 상태라면 컨텍스트는 존재하지만 내부에 값이 없다고 할 수 있다

 

// MARK: 컨텍스트
func add(_ num: Int) -> Int {
    return num + 3
}

print(add(2))       //순수값 2를 전달하면 정상적으로 함수를 실행할 수 있다
// print(Optional(2)) 순수값이 아닌 컨텍스트로 둘러싸인 값이 전달되었기 때문에 오류


// map 메서드를 사용하여 옵셔널 연산
print(Optional(2).map(add)) //Optional(5)
//map함수는 연산 결과를 다시 컨텍스트에 넣어 반환

 


함수객체

닫힌 함수객체(Endofunctor)

  • 자신의 컨텍스트와 같은 컨텍스트의 형태로 맵핑할 수 있는 함수객체
  • 고차함수인 map을 적용 할 수 있는 컨테이너 타입
  • 모나드는 닫힌 함수객체이다

 

플랫맵(flatMap) 메서드 활용

// MARK: flatMap 활용
func doubledEven(_ num: Int) -> Int? {
    if num.isMultiple(of: 2) {
        return num * 2
    }
    return nil
}

print(Optional(6).flatMap(doubledEven(_:)))     //Optional(12)
print(Optional(3).flatMap(doubledEven(_:)))     //nil

 

맵과 플랫맵(컴펙트맵)의 차이점


/* map과 flatMap의 차이 */
// 플랫맵(컴팩트맵)은 컨텍스트 내부의 컨텍스트를 모두 같은 위상으로 평평하게 펼쳐준다

let optionals: [Int?] = [1, 2, nil, 5]

let mapped: [Int?] = optionals.map{ $0 }            //[Optional(1), Optional(2), nil, Optional(5)]
let flatmapped: [Int] = optionals.compactMap{ $0 }  //[1, 2, 5]

print(mapped)       //결과를 다시 Array 컨테이너에 담는다
print(flatmapped)   //내부 컨테이너까지 값을 추출한다