Честное предупреждение, я новичок в функциональном программировании, поэтому я могу придерживаться многих неверных предположений.
Я изучал алгебраические типы. Кажется, что многие функциональные языки имеют их, и они довольно полезны в сочетании с сопоставлением с образцом. Однако какую проблему они на самом деле решают? Я могу реализовать на первый взгляд (вроде) алгебраический тип в C # следующим образом:
public abstract class Option { }
public class None : Option { }
public class Some<T> : Option
{
public T Value { get; set; }
}
var result = GetSomeValue();
if(result is None)
{
}
else
{
}
Но я думаю, что большинство согласится с тем, что это ублюдение объектно-ориентированного программирования, и вы никогда не должны этого делать. Так функциональное программирование просто добавляет более чистый синтаксис, который делает этот стиль программирования менее грубым? Что еще мне не хватает?
functional-programming
algebraic-data-type
ConditionRacer
источник
источник
class ThirdOption : Option{}
и укажу,new ThirdOption()
где вы ожидалиSome
илиNone
?data Maybe a = Just a | Nothing
(эквивалентноdata Option a = Some a | None
в вашем примере): вы не можете добавить третий случай post-hoc. В то время как вы можете эмулировать типы сумм в C #, как вы показали, это не самая красивая.Ответы:
Классы с интерфейсами и наследованием представляют открытый мир: любой может добавить новый тип данных. Для данного интерфейса могут существовать классы, реализующие его по всему миру, в разных файлах, в разных проектах, в разных компаниях. Они упрощают добавление наблюдений в структуры данных, но поскольку реализации интерфейса децентрализованы, трудно добавить новый метод в интерфейс. Когда интерфейс общедоступен, он в основном заморожен. Никто не знает всех возможных реализаций.
Алгебраические типы данных двойственны, они замкнуты . Все случаи данных перечислены в одном месте , и операции , не только могут перечислить варианты исчерпывающе, они поощряются сделать это. Следовательно, написание новой функции, работающей с алгебраическим типом данных, тривиально: просто напишите чертову функцию. В свою очередь, добавление новых случаев является сложным, потому что вам нужно пересмотреть в основном всю кодовую базу и расширить каждый
match
. Как и в случае с интерфейсами, в стандартной библиотеке Rust добавление нового варианта является серьезным изменением (для открытых типов).Это две стороны проблемы выражения . Алгебраические типы данных являются их неполным решением, как и ООП. Оба имеют свои преимущества в зависимости от того, сколько случаев данных существует, как часто эти случаи меняются, и как часто операции расширяются или изменяются. (Именно поэтому многие современные языки предоставляют оба или что-то подобное, или идут прямо к более мощным и более сложным механизмам, которые пытаются объединить оба подхода.)
источник
Возможно, это упрощение, но да.
Давайте разберемся, что такое алгебраические типы данных (суммируя эту прекрасную ссылку из «Learn you as Haskell»):
Ваш пример действительно работает только с первым.
Возможно, вам не хватает того, что, предоставляя эти две основные операции, функциональные языки позволяют создавать все остальное. C # имеет структуры, классы, перечисления, обобщения, а также кучу правил, управляющих поведением этих вещей.
В сочетании с некоторым синтаксисом, чтобы помочь, функциональные языки могут разложить операции по этим двум путям, обеспечивая чистый, простой и элегантный подход к типам.
Они решают ту же проблему, что и любая другая система типов: «какие значения здесь разрешено использовать?» - они просто придерживаются другого подхода.
источник
Вас может удивить, что сопоставление с образцом не считается самым идиоматичным способом работы с опциями. Смотрите документацию по настройкам Scala для более подробной информации. Я не уверен, почему так много учебных пособий по FP поощряют такое использование.
В основном вам не хватает того, что существует целый набор функций, созданных для облегчения работы с опциями. Рассмотрим основной пример из документов Scala:
Обратите внимание на то, как
map
иfilter
разрешить вам цепочку операций с опциями, без необходимости проверять в каждой точке, есть ли у васNone
или нет. Затем в конце вы используете,getOrElse
чтобы указать значение по умолчанию. Ни в коем случае вы не делаете что-то «грубое», например, проверку типов. Любая неизбежная проверка типов выполняется внутри библиотеки. У Haskell есть свой набор аналогичных функций, не говоря уже о большом наборе функций, которые будут работать с любой монадой или функтором.Другие алгебраические типы данных имеют свои собственные идиоматические способы работы с ними, и сопоставление с образцом в большинстве случаев является низким на полюсе тотема. Аналогично, когда вы создаете свои собственные типы, вы должны предоставлять аналогичные функции для работы с ними.
источник