Алгебраические типы данных вне функциональных языков?

Какие языки, которые не являются исключительно функциональными, имеют алгебраические типы данных (или что-то подобное) и сопоставление с образцом? Меня также интересуют языки с несколькими парадигмами - я знаю, что Ocaml и F # - это диалекты ML с добавленным OO, поэтому они наследуют алгебраические типы данных от ML.

Их можно как бы эмулировать с помощью enums и unions (как в C, C ++, ... больше?), Но вскоре это становится громоздким и уродливым, и компилятор не может предупредить вас, если вы забудете случай в сопоставлении с образцом. или (гораздо более приемлемо и гораздо опаснее) при доступе к объединению «неправильными способами», то есть вы запрашиваете поле со значением Left, когда оно на самом деле является значением Right (тогда вы получаете бессмысленную переинтерпретацию битов, которые случайно быть там).

Я слышал, что в Pascal есть что-то вроде помеченных союзов и язык Cyclone также поддерживает объединения с тегами. В Википедии также упоминаются Ada и Algol. Любые другие языки?

(Если вы никогда не слышали об алгебраических типах данных, вы можете прочитать ответ на вопрос «Что такое« сопоставление с образцом »в функциональных языках?» за отличное введение).




Ответы (5)


В Scala вы обычно используете case classes для имитации алгебраических типов данных, которые присутствуют в чисто функциональных языках, таких как ML и Haskell.

Например, следующий код F # (взятый из здесь):

type Shape =
| Circle of float
| EquilateralTriangle of double
| Square of double
| Rectangle of double * double

let area myShape =
    match myShape with
    | Circle radius -> Math.PI * radius * radius
    | EquilateralTriangle s -> (sqrt 3.0) / 4.0 * s * s
    | Square s -> s * s
    | Rectangle (h, w) -> h * w

можно примерно перевести на Scala следующим образом:

sealed abstract class Shape
case class Circle(radius: Float) extends Shape
case class EquilateralTriangle(side: Double) extends Shape
case class Square(side: Double) extends Shape
case class Rectangle(height: Double, width: Double) extends Shape

def area(myShape: Shape) = myShape match {
  case Circle(radius) => math.Pi * radius * radius
  case EquilateralTriangle(s) => math.sqrt(3.0) / 4.0 * s * s
  case Square(s) => s * s
  case Rectangle(h, w) => h * w
}

Ключевое слово sealed выше используется для того, чтобы компилятор предупредил вас, если вы забудете какое-либо case в match выражении.

person missingfaktor    schedule 19.10.2010

В языке Mozilla Rust алгебраические типы данных и сопоставление с образцом являются важными понятиями. Синтаксис тоже неплохой. Рассмотрим следующую простую программу:

static PI: f32 = 3.14159;

enum Shape {
    Circle(f32),
    Rectangle(f32, f32),
    Point
}

fn area(shape: Shape) -> f32 {
    match shape {
        Point                    => 0.0
        Circle(radius)           => PI * radius * radius,
        Rectangle(width, height) => width * height,
    }
}

fn main() {
    let radius = 4.0;
    let circle = Circle(radius);
    let area = area(circle);
    println!("The area of a circle with radius {} is {}", radius, area);
}
person Viktor Dahl    schedule 18.01.2013
comment
Синтаксис тоже неплохой - да, за исключением фигурных скобок и точки с запятой. - person Ingo; 19.01.2013
comment
@Malcolm Я хочу снизить ваш комментарий;) --- чрезмерный шум редко бывает полезным, фигурные скобки даже не информативны о том, какой блок они закрывают! Старый стиль if..fi, do..od или beginX ... endX, по крайней мере, информативен, с фигурными скобками мы, как правило, заканчиваем лестницей, которая занимает много места и совершенно бесполезна для удобочитаемости. - person Musa Al-hassy; 09.11.2016
comment
При автоматическом форматировании кода они информативны. Чтобы определить, какая скобка соответствует блоку, все, что нужно сделать, - это просмотреть страницу по вертикальной линии. Независимо от стиля, есть когнитивные издержки, связанные с пониманием того, где начинается и заканчивается область действия. Если вы используете beginX / endX, то нужно назвать все области видимости и запомнить все области по имени, чтобы перемещаться по фрагменту кода. Это то, что я вижу как добавление шума - дополнительные когнитивные накладные расходы (области именования) без каких-либо дополнительных преимуществ. - person Malcolm; 10.11.2016
comment
Я приходил к мысли использовать ключевые слова для создания блоков кода (например, if, fi, begin, end), пока не посмотрел на какой-нибудь код Ada. У фигурных скобок есть то преимущество, что они не добавляют шума. При чтении кода мы не только хотим, чтобы наш код был четко определен в некоторой иерархии (поэтому мы делаем отступ), мы также хотим, чтобы наше внимание было сосредоточено на том, что создано, а не на кучке begin / end / procedure / и т. Д. - person Harrichael; 11.03.2017
comment
Проблема со многими языками, заключенными в фигурные скобки, заключается в том, что языки, в которых они используются, поощряют длинные функции с множеством вложенных функций управления. Вместо этого язык должен поддерживать такие вещи, как функции первого класса, вывод типа (по возвращаемым функциям и параметрам) и т. Д., Чтобы поощрять более короткие функции. - person Harrichael; 11.03.2017

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

person Fred Foo    schedule 18.10.2010
comment
Спасибо - я понятия не имею о языках логики, так что я бы, вероятно, не выучил бы это сам. - person ; 19.10.2010
comment
Оба являются исследовательскими языками. Сообщество LP много заимствовало у своих кузенов FP. - person Fred Foo; 19.10.2010

Erlang имеет систему динамических типов, поэтому он не дает никаких гарантий, которые вы цитируете, но код Erlang имеет тенденцию выглядеть как продукт системы алгебраических типов:

count([]) -> 0;
count([H|T]) -> 1 + count(T).

length({rect, X, Y}) -> math:sqrt(X*X + Y*Y);
length({polar, R, _A}) -> R.
person Marcelo Cantos    schedule 18.10.2010

Я думаю, что Whiley подойдет. У whiley есть типы записей (. Т.е. Product) и объединения типов (например, сумма), следовательно.

Кажется, что сопоставление возможно только по типу, т.е. вы можете спросить, является ли значение с типом объединения одним из типов в объединении, а затем значение будет «повторно типизировано», и вы сможете получить доступ к полям того типа, который вы проверили.

person Ingo    schedule 31.07.2013