대수 데이터 타입, 합타입, 곱타입이 뭘까

이현재·2024년 1월 21일
2

대수 데이터 타입 (Algebraic Data Types - ADTs)

대수 데이터 타입(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

profile
코드 보는걸 좋아합니다. 궁금한게 많습니다.

0개의 댓글