Алгебраические типы данных Haskell - PullRequest
58 голосов
/ 19 августа 2008

Я пытаюсь полностью понять все концепции Хаскелла.

Чем алгебраические типы данных похожи на универсальные типы, например, в C # и Java? И чем они отличаются? Что в них такого алгебраического?

Я знаком с универсальной алгеброй и ее кольцами и полями, но у меня есть только смутное представление о том, как работают типы Хаскеля.

Ответы [ 8 ]

100 голосов
/ 07 мая 2011

алгебраические типы данных Хаскелла названы так, поскольку они соответствуют исходной алгебре в теории категорий, что дает нам некоторые законы, некоторые операции и некоторые символы для манипуляции. Мы можем даже использовать алгебраическую нотацию для описания регулярных структур данных, где:

  • + представляет типы сумм (непересекающиеся объединения, например, Either).
  • представляет типы продуктов (например, структуры или кортежи)
  • X для одноэлементного типа (например, data X a = X a)
  • 1 для типа устройства ()
  • и μ для наименее фиксированной точки (например, рекурсивные типы), обычно неявные.

с некоторыми дополнительными обозначениями:

  • для X•X

На самом деле, вы можете сказать (вслед за Брентом Йорджи), что тип данных Haskell является регулярным, если он может быть выражен через 1, X, +, и наименьшую фиксированную точку .

С помощью этой записи мы можем кратко описать множество обычных структур данных:

  • Единицы: data () = ()

    1

  • Опции: data Maybe a = Nothing | Just a

    1 + X

  • Списки: data [a] = [] | a : [a]

    L = 1+X•L

  • Бинарные деревья: data BTree a = Empty | Node a (BTree a) (BTree a)

    B = 1 + X•B²

Другие операции удерживаются (взято из статьи Брента Йорги, перечисленной в ссылках):

  • Расширение: развертывание точки исправления может быть полезно для размышлений о списках. L = 1 + X + X² + X³ + ... (то есть списки либо пустые, либо имеют один элемент, либо два элемента, либо три, или ...)

  • Композиция, , данные типы F и G, композиция F ◦ G - это тип, который строит «F-структуры, сделанные из G-структур» (например, R = X • (L ◦ R), где L - это списки, это розовое дерево.

  • Дифференциация, производная типа данных D (заданная как D ') - это тип D-структур с одной «дырой», то есть выделенное местоположение, не содержащее никаких данных. Это удивительно удовлетворяет тем же правилам, что и для дифференциации в исчислении:

    1′ = 0

    X′ = 1

    (F + G)′ = F' + G′

    (F • G)′ = F • G′ + F′ • G

    (F ◦ G)′ = (F′ ◦ G) • G′


Ссылка:

22 голосов
/ 19 августа 2008

«Алгебраические типы данных» в Haskell поддерживают полный параметрический полиморфизм , который является более технически правильным именем для дженериков, в качестве простого примера тип данных списка:

 data List a = Cons a (List a) | Nil

Эквивалентно (насколько это возможно, без учета нестрогой оценки и т. Д.)

 class List<a> {
     class Cons : List<a> {
         a head;
         List<a> tail;
     }
     class Nil : List<a> {}
 }

Конечно, система типов Haskell позволяет более ... интересно использовать параметры типа, но это всего лишь простой пример. Что касается имени «алгебраического типа», я, честно говоря, никогда не был полностью уверен в точной причине, по которой они названы так, но предположил, что это связано с математической основой системы типов. Я верю , что причина кроется в теоретическом определении ADT, являющегося "продуктом набора конструкторов", однако прошло несколько лет с тех пор, как я сбежал из университета, поэтому я больше не могу вспомнить особенности .

[Редактировать: Спасибо Крису Конвею за указание на мою глупую ошибку, ADT, конечно, являются типами сумм, конструкторами, предоставляющими продукт / кортеж полей]

20 голосов
/ 14 марта 2009

В Универсальная алгебра алгебра состоит из нескольких наборов элементов (думать о каждом наборе как о наборе значений типа) и некоторые операции, которые отображают элементы на элементы.

Например, предположим, что у вас есть тип "элементов списка" и тип "списки". В качестве операций у вас есть «пустой список», который является 0-аргументом функция, возвращающая «список», и функцию «против», которая принимает два аргумента, «элемент списка» и «список» и создают «список».

На данный момент есть много алгебр, которые соответствуют описанию, как могут произойти две нежелательные вещи:

  • В наборе "список" могут быть элементы, которые невозможно построить из «пустого списка» и «минусовки», так называемого «мусора». Это могут быть списки, начинающиеся с какого-то элемента, упавшего с неба, или циклы без начала или бесконечные списки.

  • Результаты применения «против» к различным аргументам могут быть равны, например добавление элемента в непустой список может быть равен пустому списку. Это иногда называют «путаницей».

Алгебра, которая не имеет ни одного из этих нежелательных свойств, называется initial , и это подразумеваемое значение абстрактного типа данных.

Имя начальное происходит от свойства, которое существует точно один гомоморфизм из начальной алгебры в любую данную алгебру. По сути, вы можете оценить значение списка, применяя операции в другой алгебре, и результат четко определен.

Это становится более сложным для полиморфных типов ...

12 голосов
/ 16 марта 2009

Простая причина, почему они называются алгебраическими; Существуют как суммы (логическое дизъюнкция), так и тип продукта (логическое соединение). Тип суммы - это дискриминационное объединение, например:

data Bool = False | True

Тип продукта - это тип с несколькими параметрами:

data Pair a b = Pair a b

В O'Caml «продукт» сделан более явным:

type 'a 'b pair = Pair of 'a * 'b
8 голосов
/ 20 августа 2008

Типы данных Haskell называются "алгебраическими" из-за их связи с категориальными начальными алгебрами . Но в этом и заключается безумие.

@ olliej: ADT на самом деле являются типами типа sum. Кортежи являются продуктами.

3 голосов
/ 30 августа 2008

@ Timbo:

В принципе, вы правы в том, что это что-то вроде абстрактного класса Tree с тремя производными классами (Empty, Leaf и Node), но вам также необходимо обеспечить гарантию того, что кто-то, использующий ваш класс Tree, никогда не сможет добавить какой-либо новые производные классы, так как стратегия использования типа данных Tree заключается в написании кода, который переключается во время выполнения на основе типа каждого элемента в дереве (и добавление новых производных типов нарушило бы существующий код). Вы можете вообразить, что это становится неприятным в C # или C ++, но в Haskell, ML и OCaml это является ключевым моментом в дизайне языка и синтаксисе, поэтому стиль кодирования поддерживает его гораздо более удобным способом, путем сопоставления с образцом.

ADT (типы сумм) также похожи на теговые объединения или варианты типов в C или C ++.

2 голосов
/ 20 декабря 2008

старый вопрос, но никто не упомянул обнуляемость, которая является важным аспектом алгебраических типов данных, возможно, самым важным аспектом. Поскольку каждое значение чаще всего является одной из альтернатив, возможно исчерпывающее сопоставление с образцом на основе регистра.

0 голосов
/ 19 августа 2008

Для меня концепция алгебраических типов данных Haskell всегда выглядела как полиморфизм в ОО-языках, таких как C #.

Посмотрите на пример из http://en.wikipedia.org/wiki/Algebraic_data_types:

data Tree = Empty 
          | Leaf Int 
          | Node Tree Tree

Это может быть реализовано в C # как базовый класс TreeNode с производным классом Leaf и производным классом TreeNodeWithChildren, и если вам нужен даже производный класс EmptyNode.

(Хорошо, я знаю, никто бы этого не сделал, но, по крайней мере, вы могли бы это сделать.)

...