티스토리 뷰

iOS

Swift) Monard

hyun99999 2022. 5. 5. 10:51
728x90
반응형

Monad(모나드)]

출처: [위키백과 - 모나드](https://ko.wikipedia.org/wiki/%EB%AA%A8%EB%82%98%EB%93%9C)

Monadic(모나딕)

모나드는 여러 영역에서 다양한 뜻을 가지기도 하고 한 문장으로 설명하기 어려운 개념이라서 범주론의 모나드 개념을 차용한 정도로 생각하면 될 것 같습니다.

모나드는 순서가 있는 연산을 처리할 때 자주 활용하는 디자인 패턴입니다.

프로그래밍에서는 모나드의 성질의 대부분을 갖추었다고 하여 모나드를 모나딕(monadic) 이라고 표현합니다. (모나딕 타입 혹은 모나딕 함수)

🧬 모나드
함수 내부에서 발생할 수 있는 사이드이펙트를 결과 집합과 함께 포함하는 타입

함수형 프로그래밍을 이해하는데 모나드는 도움이 될거에요! 결과로 항상 모나드를 반환하여 순수함수 성질을 잃지 않게 하는 것이기 때문입니다.

🧬 순수함수
외부 상태에 의존적이지 않고, 어떠한 사이드이펙트도 발생시키지 않는 함수
순수함수는 언제 얼마나 많이 호출해도 항상 같은 매개변수에 대해 같은 결과를 반환합니다.

즉, Int, String, Float, Bool, Class, Struct 를 사용해서 함수의 파라미터와 리턴값을 표현했는데 한 번 더 추상화한 것이 모나드 입니다.

프로그래밍에서 모나드가 갖춰야 하는 조건

1️⃣ 타입을 인자로 받는 타입(특정 타입의 값을 포장) → 제네릭

2️⃣ 특정 타입의 값을 포장한 것을 반환하는 함수(메서드)가 존재

3️⃣ 포장된 값을 변환하여 같은 형태로 포장하는 함수(메서드)가 존재

💫 Optional(옵셔널) 은 모나드의 예시 입니다.

스위프트에서 모나드를 사용한 예시 중 하나가 옵셔널 입니다.

옵셔널은 열거형!

: 값이 있을지 없을지 모르는 상태를 포장하는 것.

Context(컨텍스트)

Contents(콘텐츠) 를 담는 그 무언가.

옵셔널을 떠올리면 값이 없다면 .none case 로 있다면 .some(value) case 로 값을 지닙니다.

  • 2라는 숫자를 옵셔널로 둘러싸면? → 컨텍스트 안에 2라는 콘텐츠가 들어가는 모양입니다.
  • 값이 없는 옵셔널이라면? → 컨텍스트는 존재하지만 내부 값이 없다.

조건 충족

1️⃣ 옵셔널은 Wrapped 타입을 인자로 받는 제네릭 타입.

2️⃣ 콘텐츠의 타입을 갖는 컨텍스트를 반환.

3️⃣ map! map?

🧬 map 은 컨테이너(다른 타입의 값을 담을 수 있으므로 컨텍스트 역할을 할 수 있다.)의 값을 변형시킬 수 있는 고차함수
옵셔널은 컨테이너와 값을 갖기 때문에 map 함수를 사용할 수 있다.

var value: Int? = 2
value.map { $0 + 3 } // Optional(5)
value = nil
value.map { $0 + 3 } // nil

💫 또한, 모나드는 닫힌 함수객체입니다.

함수 객체?(Functor)

을 적용할 수 있는 컨테이너 타입. 즉, 함수 객체는 포장된 값에 함수를 적용할 수 있습니다.

닫힌 함수객체?(Endofunctor)

자신의 컨텍스트와 같은 컨텍스트의 형태로 맵핑할 수 있는 함수 객체를 닫힌 함수객체 (Endofunctor)라고 합입니다.

모나드닫힌 함수객체입니다.

즉, 모나드는 결과가 함수객체와 같은 컨텍스트를 반환하는 함수객체라고 할 수 있고, 이런 맵핑을 수행하도록 flatMap 을 활용합니다.

flatMap

맵과 같이 함수를 매개변수로 받지만, 컨텍스트 내부의 컨텍스트를 모두 같은 위상으로 평평하게 펼쳐준다는 차이가 있습니다.

flatMap 을 사용해 보겠습니다.

// 옵셔널과 flatMap 을 사용할 수 있나요?
// 네! 옵셔널은 모나드이기 때문이죠.

let optionalArray: [Int?] = [1, 2, 3, 4, nil]
// [Optional(1), Optional(2), Optional(3), Optional(4), nil]

let flatMappedArray = optionalArray.flatMap { $0 }
// [1, 2, 3, 4]

// 2차원 배열을 1차원 배열로 flatten 하게 만들어준다.
let nestedArray: [[Int]] = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flatMappedNestedArray = nestedArray.flatMap { $0 }
// [1, 2, 3, 4, 5, 6, 7, 8, 9]

let nestedNilArray: [[Int?]] = [[1, 2, nil], [4, nil, nil], [7, 8, 9]]
let flatMappedNestedNilArray = nestedNilArray.flatMap { $0 }
// [Optional(1), Optional(2), nil, Optional(4), nil, nil, Optional(7), Optional(8), Optional(9)]

let optional: Int? = 8
let value = optional.flatMap { $0 > 0 ? $0 : -$0 }
// Optional(8)

flatMap 의 기능

  1. non-nil 의 배열을 리턴
  2. 주어진 Sequence 내의 요소들을 하나의 배열로써 리턴
  3. 주어진 Optional 이 not-nil 인지 판단 후 unwrapping 하여 closure 파라미터로 전달

⚠️경고

2

💫 flatMap(_:)

  • Deprecated. non-nil 배열을 리턴하는 기능이 dprecated 되었습니다.3
  • 위의 기능을 제외한 클로저가 옵셔널 변수를 리턴하지 않는 경우는 flatMap 을 그대로 사용하면 되겠습니다.4

💫 compactMap

  • non-nil 배열을 리턴.(deprecated 된 flatMap 과 declaration 이 동일하다.)

5

flatMap 과 Map 이랑 다른점은?

  • 정수형으로 변환하기위해서 Int() 를 사용해서 int 옵셔널변수를 리턴하는 경우
// flatMap 은 클로저를 실행하면 내부 컨테이너까지 값을 추출한다.
let optionalInt: String? = "3"
let flatMappedOptionalString = optionalInt.flatMap { Int($0) }
// Optional(3)

// map 은 클로저 코드를 실행하고, 결과를 다시 컨테이너에 담기만 한다.
let mappedOptionalString = optionalInt.map { Int($0) }
// Optional(Optional(3))

출처:

  • 스위프트 프로그래밍(3판) / 야곰

[Swift] 순수함수(Pure Function)란?

모나드의 이해

[Swift] 고차함수(2) - map, flatMap, compactMap

728x90
반응형
댓글
최근에 올라온 글
최근에 달린 댓글
글 보관함
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
링크
Total
Today
Yesterday