Алгебраические типы данных вне функциональных языков?
Какие языки, которые не являются исключительно функциональными, имеют алгебраические данные
типы (или что-то подобное) и сопоставление образцов? Меня также интересуют языки с несколькими парадигмами - я знаю, что Ocaml и F # - это диалекты ML с добавлением OO, поэтому они наследуют алгебраические типы данных от ML.
Они могут быть эмулированы с использованием enum
и union
(как в C, С++,... больше?), но это скоро становится громоздким и уродливым, и компилятор не может вас предупредить, если вы забудьте случай в вашем сопоставлении с образцом или (гораздо более уязвимый и более опасный) при доступе к союзу "неправильно", т.е. вы запрашиваете поле значения Left
, когда оно фактически является значением Right
(что вы получаете тогда бессмысленную переинтерпретацию битов, которые бывают там).
Я слышал, что Паскаль имеет что-то вроде помеченных объединений и Язык Cyclone поддерживает тегированные союзы. Википедия также упоминает Аду и Алгол. Любые другие языки?
(Если вы никогда не слышали об алгебраических типах данных, вы можете прочитать ответ на вопрос "Что такое" соответствие шаблонов "на функциональных языках?" для отличного введения).
Ответы
Ответ 1
В Scala вы обычно используете case class
es для эмуляции алгебраических типов данных, как это показано на реальных синих функциональных языках, таких как 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
.
Ответ 2
В 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);
}
Ответ 3
Язык логического программирования Mercury называет их дискриминационными объединениями. Язык ограничения CHR, встроенный в Prolog, тоже имеет их, но там они полностью необязательны, общие термины Prolog являются типом по умолчанию.
Ответ 4
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.
Ответ 5
Я думаю, что Whiley будет квалифицироваться.
В то время как у Новых есть типы записей (например, продукт) и объединения типов (т.е. Сумма), следовательно.
Соответствие возможно только по типу, т.е. вы можете спросить, является ли значение с типом union одним из типов в объединении, а затем значение "перепечатывается", и вы можете получить доступ к полям типа, который вы проверен.