Swift의 데이터 타입과 대수학(Algebra)의 관계에 대해서 알아보도록 하겠습니다.
데이터 타입과 대수학이 관계가 깊다는 얘기를 우연히 접한적이 있습니다. 데이터 타입은 제가 String, Int, Bool, Long, Short, unsigned Int 등 문자열, 실수 정수 등 여러 종류의 데이터를 나타내기 위한 것이다라는 생각을 바로 할 수 있었는데요
대수학은 뭘까요??…. 나무위키에 따르면 초중등교육에서는 미지수에 변수를 대입하는 기술이라고 하는데.... 그 이상의 개념은 저는 잘 모르겠어서 암튼 여기까지 하고 일단 넘어가 보겠습니다.
구조체와 대수학
먼저 아래와 같은 구조체를 정의해보겠습니다.
struct Some<T, U> {
let first: T
let second: U
}
위 구조체는 제네릭 타입으로 T, U를 받고 first와 second라는 2개의 필드에 각각 T, U의 타입을 가지고 있습니다.
만약 T와 U가 Boolean값이라면 Some이라는 구조체가 가질 수 있는 경우의 수는 총 4가지 입니다.
(true, true), (true, false), (false, true), (false, false)
만약 Boolean을 T에 3가지의 경우를 가질 수 있는 열거형을 U타입에 넘기면 어떻게 될까요?
enum SomeEnum {
case a
case b
case c
}
Some<Bool, SomeEnum>(first: true, second: .a)
Some<Bool, SomeEnum>(first: true, second: .b)
Some<Bool, SomeEnum>(first: true, second: .c)
Some<Bool, SomeEnum>(first: false, second: .a)
Some<Bool, SomeEnum>(first: false, second: .b)
Some<Bool, SomeEnum>(first: false, second: .c)
이렇게 총 6가지의 경우가 나옵니다. 2*3이죠
만약 U의 위치에 Void를 넣으면 어떻게 될까요?? Void는 경우의 수가 () 한 개뿐이므로
2*1의 2가지 경우가 나오죠.
Void는 수학적으로 1을 의미한다고 해석하면 될 것 같기도 합니다?!
그렇다면 0은 어떻게 표현할까요??
간단합니다. Never라는 키워드를 Result타입의 Failure타입에서 보신적이 있으실 거에요.
Failure타입에 있을 경우 발생하지 않는다는 뜻을 나타내게 되는데요. Never의 실제 구현은 아래와 같습니다.
@frozen enum Never { }
케이스가 없는 열거형이네요.
실제로 아래와 같이 U에 Never를 넘길 경우 아예 구조체 인스턴스를 만들 수 조차 없습니다.
Some<Bool, Never>(first: true, second: ...???)
자 잠깐 정리하면
Bool은 2, Void는 1, Never는 0 이네요.
그리고 구조체에서는 T와 U의 경우의 수의 곱만큼 실제 구조체가 가질 수 있는 경우의 수가 존재하게 됩니다.
<Bool, Bool> = 2*2
<Bool, Void> = 2*1
<Void, Void> = 1*1
<Bool, Never> = 2*0
열거형과 대수학
열거형에서는 어떻게 될까요
enum ExampleEnum<T, U> {
case a(T)
case b(U)
}
위와 같은 열거형이 있다고 해봅시다. 그리고 구조체에서와 같이 아래의 총 4개의 열거형을 정의해보겠습니다.
ExampleEnum<Bool, Bool>
ExampleEnum<Bool, Void>
ExampleEnum<Void, Void>
ExampleEnum<Bool, Never>
그러면 열거형이 가질 수 있는 경우의 수는 아래와 같습니다
ExampleEnum<Bool, Bool>.a(true)
ExampleEnum<Bool, Bool>.a(false)
ExampleEnum<Bool, Bool>.b(true)
ExampleEnum<Bool, Bool>.b(false)
// 2+2
ExampleEnum<Bool, Void>.a(true)
ExampleEnum<Bool, Void>.a(false)
ExampleEnum<Bool, Void>.b(())
// 2+1
ExampleEnum<Void, Void>.a(())
ExampleEnum<Void, Void>.b(())
// 1+1
ExampleEnum<Bool, Never>.a(true)
ExampleEnum<Bool, Never>.a(false)
// 2+0
구조체와 다르게 곱연산이 아니라 합연산만큼 경우의 수가 있는 것을 볼 수 있습니다.
Optional을 예로 들어보겠습니다.
enum Optional<A> {
case none
case some<A>
}
여기서 none은 none(Void)로 볼 수 있을 것 같습니다. Optional.none(())와 Optional.none은 언제나 한가지의 경우만 존재하니까요.
그래서 Optional<A>의 경우의 수는 1 + A의 경우의 수만큼 존재할 수 있음을 알 수 있고 이를
Optional<A> = A? = 1 + A = Void + A
이렇게 볼 수 있겠네요
이걸 아는게 왜 중요할까요???
결론부터 얘기하면 실제 서비스를 만들기 위해 데이터를 모델링할 때 존재하지 않을 케이스를 만들지 않아서 불필요한 작업을 사전에 방지하는데 도움이 되기 때문입니다.
유저 정보를 API호출로 받아오는데 거기에는 이름과 나이 전화번호를 준다고 해봅시다.
또 전화번호는 국가코드와 국가코드 번호 그리고 숫자 번호가 있다고 해봅시다. 그리고 전화번호는 유저가 회원가입 시 입력할 수도 있고 안 할수도 있습니다.
이를 모델링 해보겠습니다.
첫 번째 방법
struct UserInfo {
let name: String
let age: Int
let phoneNationalCode: String?
let phoneNationalCodeNumber: String?
let phoneNumber: String?
}
두 번째 방법
struct UserInfo {
let name: String
let age: Int
var phoneNumber: PhoneNumber?
}
struct PhoneNumber {
let nationalCode: String
let nationalCodeNumber: String
let number: String
}
제 생각에는 2 번째 방법이 보다 잘 도메인을 잘 표현했다고 생각이 듭니다. 단순히 optional unwrapping의 횟수를 3번에서 1번으로 줄여서가 아니라 개념적으로 동시에 존재해야만 존재할 수 있는 3가지 개념들을 제대로 표현해낸 방법이 2번째이기 때문입니다.
다른 예를 또 들어보겠습니다.
대기열 기능을 만드려고 합니다
대기열에는 다음 3가지 상태가 있습니다.
- 들어가기 전 ready상태
- 대기열에 들어가서 기다리는 waiting 상태로 이 때는 내 앞에 남은 사람의 숫자 n을 같이 보내줍니다
- 들어갈 수 있는 상태 finish
서버측에서 API Response 형상 예시를 보내왔습니다. 아래처럼 말이죠
{
"status": "ready"
}
{
"status": "waiting",
"remaining_count": 6,
}
{
"status": "finish"
}
어떻게 모델링하면 좋을까요??
첫 번째 방법
enum WaitingStatus {
case ready
case waiting
case finish
}
struct Wating {
case status: WaitingStatus
case remainingCount: Int?
}
두 번째 방법
enum WaitingStatus {
case ready
case waiting(Int)
case finish
}
당연히 2번째 방법이 좋습니다. 첫 번째 방법의 경우는 ready인데 9명이 기다리고 있다라는 상태를 가질 수 있는 가능성을 모델링에서 막아주고 있지 않기 때문입니다.
오랜만에 하는 포스팅이기 때문에 가벼운 글부터 시작해 보았는데요. 도움이 되셨을 지 모르겠네요ㅎㅎ
참고
'swift' 카테고리의 다른 글
테스트 더블이란 (2) | 2023.04.13 |
---|---|
Swift, Objective-C, C++ 같이 사용하기 (0) | 2020.04.25 |
[Tip] Xcode PROJECT_ROOT BuildSetting에서 쓰는 변수명 출력하기 (0) | 2020.04.09 |
[Swift] 원하는 앱의 document 폴더 확인하기 (0) | 2020.04.06 |