YaChudo

Тип-сума

Переглядів: 0. Оновлено 27.10.2026.

Тип-сума (англ. tagged union) — це структура даних, що зберігає значення, що може належати до одного з декількох наперед визначених типів. Лише один із типів може використовуватися одночасно, а поле тегу (англ. tag) чітко вказує, який саме тип використовується. Його можна розглядати як тип, що має кілька “варіантів”, кожен з яких має оброблятися правильно під час маніпуляцій з цим типом.

Опис

Типи-суми є важливим елементом функційного програмування, де вони можуть комбінуватися із типами-добутками, утворюючи алгебраїчні типи даних (англ. ADT). У мовах програмуваннях, таких як Haskell або Rust, компілятор перевіряє, чи всі випадки типу-суми завжди обробляються, уникаючи цим багатьох типів помилок. Проте такі типи можуть бути реалізовані і в інших мовах програмування: в об’єктно-орієнтованому програмуванні аналогом типів-сум є запечатані класи.

Разом із поняттям “тип-сума” використовується поняття “конструктора”, що має схоже значення, що і в ООП, проте частково відрізняється. Конструктор типу-суми — це функція, що повертає значення типу-суми, враховуючи тег і значення відповідного типу. Наприклад,

data MyType = A Int | B

Це тип MyType, в якому конструктор A — це функція 1 аргумента типу Int, що повертає значення типу MyType, а конструктор B — функція 0 аргументів. Окремим випадком є “тип-перелік”, що являє собою тип-суму одиничних типів та відповідає множині конструкторів нульової арності, що не несуть в собі ніякої додаткової інформації окрім значення тегу.

Математичне значення

Математично, типи-суми відповідають диз’юнктивним об’єднанням, що зазвичай записуються з використанням + {\textstyle +} . Якщо дано елемент диз’юнктивного об’єднання A + B {\textstyle A+B} , можна визначити, чи походить він з A {\textstyle A} , чи з B {\textstyle B} . Якщо елемент знаходиться в обох множинах, то в A + B {\textstyle A+B} існуватимуть дві фактично різні копії значення, одна в A {\textstyle A} і одна в B {\textstyle B} . У теорії типів, тип-сума A + B {\textstyle A+B} має дві ін’єкції:

inj 1 : A → A + B , inj 2 : B → A + B . {\displaystyle {\begin{aligned}&{\text{inj}}_{1}\colon A\rightarrow A+B,\\&{\text{inj}}_{2}\colon B\rightarrow A+B.\end{aligned}}}

Форма усунення (спосіб використання значень цього типу) в даному випадку є “аналізом випадків”, відома також як pattern matching у функційних мовах, таких як Haskell:

якщо  e : A + B , e 1 : τ    при  ( x : A ) , e 2 : τ    при  ( y : B ) , тоді  c a s e   e   o f   { x ⇒ e 1 y ⇒ e 2 : τ {\displaystyle {\begin{aligned}&{\text{якщо }}e\colon A+B,\\&\quad e_{1}\colon \tau \ {\text{ при }}(x:A),\\&\quad e_{2}\colon \tau \ {\text{ при }}(y:B),\\&{\text{тоді }}{\mathsf {case}}\ e\ {\mathsf {of}}\ {\begin{cases}x\Rightarrow e_{1}\\y\Rightarrow e_{2}\end{cases}}\colon \tau \end{aligned}}}

Переваги та недоліки

Типи-суми забезпечують безпечний доступ до даних: компілятор перевіряє, який варіант типу є активним, і перевіряє, чи всі альтернативи оброблені. Вони також зменшують вимоги до пам’яті, зазвичай виділяючи місце лише для найбільшого значення.

Їхнім головним недоліком є додаткова пам’ять, необхідна для зберігання тегу, хоча вона зазвичай невелика (кілька бітів). У низькорівневих системах іноді використовуються обчислювані або згорнуті теги, щоб уникнути цих витрат. На відміну від нетегованих об’єднань типи-суми не призначені для реінтерпретації даних на бітовому рівні.

Типи-суми можуть також використовуватися для обробки “виняткових значень”, на кшталт опціональних типів або обробки винятків. Таке використання типів-сум можна формалізувати у вигляді монади:

return : A → ( A + E ) = a ↦ value ⁡ a , bind : ( A + E ) → ( A → ( B + E ) ) → ( B + E ) = = a ↦ f ↦ { err ⁡ e , якщо  a = err ⁡ e , f a ′ , якщо  a = value ⁡ a ′ . {\displaystyle {\begin{aligned}{\text{return}}\colon &A\to (A+E)=a\mapsto \operatorname {value} a,\\[1ex]{\text{bind}}\colon &(A+E)\to (A\to (B+E))\to (B+E)=\\&\qquad =a\mapsto f\mapsto {\begin{cases}\operatorname {err} e,&{\text{якщо }}a=\operatorname {err} e,\\f\,a',&{\text{якщо }}a=\operatorname {value} a'.\end{cases}}\end{aligned}}}

Приклад

Скажімо, ми хочемо побудувати двійкове дерево цілих чисел. У Haskell ми б зробили це, створивши такий тип даних:

data Tree = Leaf Int | Node Tree Tree

Це тип-сума, що містить два випадки: листок (Leaf), що містить число, і вузол (Node), що містить в собі два підвузли, які в свою чергу можуть бути як Leaf, так і Node. Leaf та Node — це конструктори, які дозволяють нам фактично створювати довільні дерева, наприклад:

Node
    (Node (Leaf 1) (Leaf 2))
    (Node (Leaf 3) (Node (Leaf 4) (Leaf 5)))

що відповідає даному дереву:

Приклад двійкового дерева з Tree

Див. також

Зовнішні посилання

Джерело: стаття у Вікіпедії та історія редагувань (автори).