Я долгое время программист ОО и новичок в области функционального программирования. Из моего небольшого разоблачения алгебраические типы данных для меня выглядят только как особый случай наследования, когда у вас есть только одна иерархия уровней, и суперкласс не может быть расширен за пределы модуля.
Вы описываете типы с замкнутой суммой, наиболее распространенную форму алгебраических типов данных, как видно из F # и Haskell. По сути, все согласны с тем, что они являются полезной функцией в системе типов, прежде всего потому, что сопоставление с образцом позволяет легко разбивать их по форме, а также по содержанию, а также потому, что они позволяют проводить исчерпывающую проверку и проверку избыточности.
Однако существуют и другие формы алгебраических типов данных. Важным ограничением общепринятой формы является то, что они являются закрытыми, что означает, что ранее определенный тип закрытой суммы не может быть расширен с помощью конструкторов новых типов (часть более общей проблемы, известной как «проблема выражения»). Полиморфные варианты OCaml допускают как открытые, так и закрытые типы сумм и, в частности, вывод типов сумм. Напротив, Haskell и F # не могут вывести типы сумм. Полиморфные варианты решают проблему экспрессии, и они чрезвычайно полезны. Фактически, некоторые языки построены полностью на расширяемых алгебраических типах данных, а не на типах с замкнутой суммой.
В крайнем случае, у вас также есть языки, такие как Mathematica, где "все является выражением". Таким образом, единственный тип в системе типов образует тривиальную «одноэлементную» алгебру. Это «расширяемое» в том смысле, что оно бесконечно, и, опять же, оно завершается совершенно другим стилем программирования.
Итак, мой (потенциально глупый) вопрос: если ADT - это просто особый случай наследования (опять же, это предположение может быть неправильным; пожалуйста, исправьте меня в этом случае), тогда почему наследование получает всю критику, а ADT - все похвалы?
Я полагаю, что вы имеете в виду именно наследование реализации (т.е. переопределение функциональности родительского класса), а не наследование интерфейса (то есть реализацию согласованного интерфейса). Это важное различие. Наследование реализации часто ненавидят, тогда как наследование интерфейса часто любят (например, в F #, который имеет ограниченную форму ADT).
Вы действительно хотите и ADT, и наследование интерфейса. Такие языки, как OCaml и F #, предлагают оба варианта.