대수 데이터 타입(ADT)은 타입 이론과 이를 지원하는 프로그래밍 언어의 기본 개념입니다. ADT는 타입을 구성하여 유연하면서도 타입 안전성을 갖춘 복잡한 데이터 구조를 만드는 방법입니다. 두 가지 기본 연산을 사용하여 생성되기 때문에 "대수적"이라고 불립니다: "합계" 및 "곱하기"는 각각 논리 OR 및 AND에 해당합니다.
ADT는 복합 타입으로, 다른 타입을 결합하여 정의됩니다. 이를 통해 개발자는 작업 중인 도메인을 정확하게 모델링하는 방식으로 새로운 타입을 정의할 수 있습니다. 예를 들어 ADT는 여러 타입(합타입
) 또는 여러 타입의 조합(곱타입
) 중 하나를 선택할 수 있습니다.
ADT의 중요성은 프로그램에서 정확성을 보장하는 표현력 있고 강력한 타입 구성을 가능하게 하는 능력에 있습니다.
대수 데이터 타입을 통해 사용자가 정의하는 데이터 구조가 유효한 상태만을 나타내도록 설계할 수 있습니다. 컴파일 타임 보증을 통해 소프트웨어가 더 예측 가능해지고, 오류 발생 가능성이 줄어듭니다.
ADT는 데이터에 대해 잘 정의된 구조를 제공함으로써 코드베이스를 더 명확하고 예측 가능하게 만들 수 있습니다.
ADT의 유연성 덕분에 데이터 구조를 일반적으로 정의할 수 있어 프로그램의 다른 부분이나 프로젝트 전반에서 재사용할 수 있습니다.
합타입은 ADT 유형의 두가지 기본 요소 중 하나입니다. 합타입은 여러 타입 중 하나에 해당하는 값을 가질 수 있지만 한번에 하나 이상은 가질 수 없습니다.
Shape은 원, 직사각형, 삼각형 중 하나만 될 수 있습니다.
data Shape = Circle Float | Rectangle Float Float | Triangle Float Float Float
enum Shape {
Circle(f32),
Rectangle(f32, f32),
Triangle(f32, f32, f32),
}
type Shape =
| { kind: "circle"; radius: number; }
| { kind: "rectangle"; width: number; height: number; }
| { kind: "triangle"; a: number; b: number; c: number; }
합타입은 여러 가지 시나리오를 모델링할 때 매우 유용합니다. 예를 들어, 애플리케이션의 다양한 상태(예: 로딩 중, 성공, 오류)나 메시징 시스템의 다양한 종류의 메시지(예: 텍스트, 이미지, 동영상)를 나타낼 수 있습니다.
안정성: 컴파일러가 모든 가능성을 처리하여 런타임 오류의 가능성을 줄일 수 있습니다.
명확성: 합계 유형은 애플리케이션 내의 다양한 상태 또는 옵션을 명시적으로 표현하여 가독성을 향상시킵니다.
패턴매칭: 합계 유형이 있는 많은 언어가 패턴 매칭도 지원하므로 합계 유형 값을 분해하여 각 변형을 다르게 처리할 수 있습니다.
패턴 매칭은 합타입과 함께 사용됩니다. 이 기능을 사용하면 합타입의 각 변형을 해체하고 다르게 처리할 수 있습니다. 패턴 매칭은 제어 흐름을 제공할 뿐만 아니라 모든 변형이 처리되도록 보장하여 타입 안전성을 향상시키는 강력한 기능입니다.
data Shape = Circle Float | Rectangle Float Float
area :: Shape -> Float
area shape = case shape of
Circle radius -> pi * radius * radius
Rectangle width height -> width * height
enum WebEvent {
PageLoad,
PageUnload,
KeyPress(char),
Paste(String),
Click { x: i64, y: i64 },
}
fn inspect(event: WebEvent) {
match event {
WebEvent::PageLoad => println!("page loaded"),
WebEvent::PageUnload => println!("page unloaded"),
WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
WebEvent::Paste(s) => println!("pasted \"{}\".", s),
WebEvent::Click { x, y } => {
println!("clicked at x={}, y={}.", x, y);
},
}
}
곱타입은 ADT의 또 다른 주요 요소입니다. 곱타입은 각각 다른 타입일 수 있는 여러 데이터를 결합한 값을 설명합니다.
아래 예시에서, 한 사람이 이름과 나이, 주소를 가지고 있다면 이는 논리적 AND입니다.
data Person = Person String Int -- 이름 나이
struct Person {
name: String,
age: u8,
}
type Person = {
name: string;
age: number;
}
조직화: 관련 데이터를 한 곳에 모아두기 때문에 데이터 관리가 간소화됩니다.
타입 안전성: 곱타입의 각 필드는 타입이 확인되므로 데이터 순서가 뒤섞이거나 잘못된 타입이 할당되는 것을 방지할 수 있습니다.
편의성: 일반적으로 필드 이름을 사용하여 곱타입의 데이터에 액세스하기 때문에 매우 간단합니다.
여기서 Status는 다양한 사용자 상태를 나타내는 열거형이고 User는 Status를 포함하는 구조체입니다. print_user_info 함수는 사용자 상태에 대한 패턴 매칭을 사용하여 다양한 메시지를 인쇄합니다.
// 합타입
enum Status {
Active,
Inactive,
Banned(String),
}
// 곱타입
struct User {
name: String,
age: u8,
status: Status,
}
// 패턴매칭
fn print_user_info(user: &User) {
match user.status {
Status::Active => println!("User {} is active.", user.name),
Status::Inactive => println!("User {} is inactive.", user.name),
Status::Banned(ref reason) => println!("User {} is banned: {}.", user.name, reason),
}
}
이처럼 ADT는 강력하고 형식에 안전한 데이터 구조를 생성하기 위한 프레임워크를 제공함으로써 개발자가 모델링하려는 도메인을 정확하게 반영하는 보다 명확하고 유지 관리가 용이한 코드를 작성할 수 있도록 지원합니다.
참고
Wikipedia: Algebraic data type
Algebraic Data Types: Things I wish someone had explained about functional programming
The Algebra of Algebraic Data Types, Part 1
Mastering sum types
A brief introduction to the Algebra of Types