Что такое дискриминационный союз в F # и какой тип альтернативы у нас в OOP - PullRequest
4 голосов
/ 01 марта 2020

Я вхожу в функциональное программирование с C#. Из-за моего глубокого и детального знания C#, конечно, я выбрал свой первый функциональный язык F # и попытался потратить свое время на его изучение.

Теперь я нахожусь на этапе, когда мне нужно понять, что такое Дискриминационные союзы и почему это важно и зачем нам это действительно нужно?!

Я сделал действительно много исследований

Но проблема наставников, лекторов, статей и постов в блогах заключается в том, что люди на самом деле пытаются описать / научить нас Дискриминационные союзы с множеством функциональных программных терминов, которые это, конечно, очень непонятно для нас, людей, чей общий опыт OOP и просто немного LINQ, выражений и функций высокого порядка.

Я очень новичок в функциональном мире, и мой мозг полон этого OOP мышления, поэтому очень трудно просто понять эту концепцию с этой точки зрения.

Если вы на самом деле google , вы получите ответ такого типа:

Дискриминационные объединения Также известен как теговые союзы или алгебраические типы данных c. Дискриминационные союзы полезны в функциональном программировании.

И это действительно не имеет никакого смысла в моей голове. Поэтому, пожалуйста, по-человечески и скажите, что такое Дискриминационный Союз, зачем он нам? Что можно сравнить с миром OOP? (потому что это действительно поможет мне)

Спасибо.

Ответы [ 3 ]

7 голосов
/ 02 марта 2020

Мир OOP на самом деле не имеет строгого аналога DU (именно поэтому его часто называют «несовершенным»), но наиболее близким является двухуровневая иерархия наследования.

Рассмотрим следующий DU:

type Shape = Circle of radius:Float | Rectangle of width:Float * height:Float

Семантику (то есть "значение") этого типа можно сформулировать примерно так: формы бывают двух видов - Circle , который имеет радиус, и Rectangle, который имеет ширину и высоту, и нет других видов фигур

Это будет примерно эквивалентно следующей иерархии наследования :

abstract class Shape {}

class Circle : Shape { 
    public double Radius { get; set; }
}

class Rectangle : Shape {
    public double Width { get; set; }
    public double Height { get; set; }
}

Этот фрагмент C# также смутно выражает идею о том, что формы бывают двух видов - Circle и Rectangle ", но есть некоторые важные различия:

  1. В будущем (или в других библиотеках) могут появиться другие виды фигур. Другие люди могут просто объявить новый класс, который наследуется от Shape - и вот вам go. Дискриминационные союзы F # этого не допускают.

  2. Circle и Rectangle являются собственными типами. Это означает, что можно объявить метод, который принимает Circle, но не Rectangle. F # дискриминационные союзы не позволяют этого. В F # Shape является типом, но Circle и Rectangle не являются типами. Не может быть переменных, параметров или свойств типа Circle.

  3. F # предоставляет ряд упрощенных конструкций syntacti c для работы с DU, которые в C# имеют быть написанным очень многословно, с большим количеством шума.


Точки (1) и (2) на поверхности кажутся ограничениями (действительно, я использовал слова "делать не разрешать "в обоих случаях), но это на самом деле особенность. Идея состоит в том, что некоторые ограничения (не все) приводят к более правильным и стабильным программам. Посмотрите в прошлое: «Перейти к списку вредных», ссылки заменяют указатели, сборка мусора заменяет ручное управление памятью - все это отнимает некоторую гибкость, а иногда и производительность, но компенсирует это повышением надежности кода.

То же самое с F # DU: тот факт, что не может быть других типов фигур, кроме Circle и Rectangle, позволяет компилятору проверять правильность функций, работающих с Shape - т.е. убедитесь, что все возможные случаи были обработаны. Если позже вы решите добавить третий тип фигуры, компилятор поможет найти все места, которые необходимо обработать в этом новом случае.


Третий пункт говорит о идее "сделать правильную вещь легкой и не ту вещь тяжело ". С этой целью F # предоставляет некоторый полезный синтаксис (например, сопоставление с образцом) и некоторые полезные значения по умолчанию (например, неизменяемость, структурное сравнение), которые в C# должны быть вручную закодированы и применены с дисциплиной.

4 голосов
/ 02 марта 2020

Дискриминационные объединения похожи на иерархии классов в OOP. Пример classi c в OOP напоминает животное, которым может быть собака или кошка. В OOP вы бы представляли это как базовый класс с некоторыми абстрактными методами (например, MakeAnimalNoise) и конкретными подклассами для собак и кошек.

В функциональном программировании подходящая вещь - это различимый союз Animal с двумя случаями:

type Animal =  
  | Dog of breed:string
  | Cat of fluffynessLevel:int

В OOP у вас есть виртуальные методы. В FP вы пишете операции как функции, используя сопоставление с образцом:

let makeAnimalNoise animal = 
  match animal with
  | Dog("Chihuahua") -> "woof sqeek sqeek woof"
  | Dog(other) -> "WOOF"
  | Cat(fluffyness) when fluffyness > 10 -> "MEEEOOOOW"
  | Cat(other) -> "meow"

Существует одно важное различие между методами FP и OOP:

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

Это может показаться странным, если вы приходите с OOP фона. При обсуждении классов в OOP все подчеркивают необходимость расширяемости (добавляя новые классы). На практике я думаю, что вам нужно и то, и другое, и поэтому не имеет большого значения, какое направление вы выберете. У FP есть свои приятные преимущества, как и у OOP (иногда).

Это, конечно, совершенно бесполезный пример. Для более реалистичного c обсуждения того, как это полезно на практике, см. Превосходную серию Скотта Влашина Проектирование с типами .

3 голосов
/ 03 марта 2020

Хотя остальные ответы в основном охватывают топи c, я думаю, что стоит добавить способ, которым это «традиционно» будет реализовано на языке ОО, - это использование шаблона посетителя. Марк Симан хорошо объясняет изоморфизм этих двух в своем блоге .

...