language agnostic - Tipos de datos algebraicos fuera de los lenguajes funcionales?
language-agnostic programming-languages (5)
Creo que Whiley calificaría. Whiley tiene tipos de registro (es decir, producto) y escribe uniones (es decir, suma), por lo tanto.
La coincidencia parece posible solo en el tipo, es decir, puede preguntar si un valor con tipo de unión es uno de los tipos en la unión y luego el valor se "vuelve a escribir" y puede acceder a los campos del tipo que seleccionó.
¿Qué idiomas que no son únicamente funcionales tienen tipos de datos algebraicos (o algo similar) y coincidencia de patrones? También me interesan los lenguajes multi-paradigma: sé que Ocaml y F # son dialectos ML con OO agregado, por lo que heredan los tipos de datos algebraicos de ML.
Pueden emularse utilizando enum
y union
(como en C, C ++, ... ¿más?), Pero esto pronto se vuelve engorroso y feo, y el compilador no puede advertirle si olvida un caso en su coincidencia de patrones o (mucho más propagable y mucho más peligroso) cuando se accede a la unión "de maneras incorrectas", es decir, se solicita un campo de un valor Left
cuando en realidad es un valor Right
(lo que se obtiene es una reinterpretación sin sentido del bits que están allí).
He oído que Pascal tiene algo así como uniones etiquetadas y el lenguaje Cyclone también admite sindicatos etiquetados. Wikipedia también menciona Ada y Algol. ¿Algún otro idioma?
(En caso de que nunca haya oído hablar de tipos de datos algebraicos, puede leer una respuesta a "¿Qué es ''Coincidencia de patrones'' en idiomas funcionales?" Para obtener una excelente introducción).
El lenguaje de programación lógica Mercurio los llama sindicatos discriminados . El idioma de restricción CHR, incrustado en Prolog, también los tiene , pero allí son totalmente opcionales, los términos generales de Prolog son del tipo predeterminado.
En Scala, normalmente se usan case class
para emular los tipos de datos algebraicos que se encuentran en los lenguajes funcionales verdaderos azules como ML y Haskell.
Por ejemplo, siguiendo el código F # (tomado de aquí ):
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
se puede traducir aproximadamente a Scala de la siguiente manera:
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
}
La palabra clave sealed
anterior se usa para que el compilador lo advierta en caso de que olvide algún case
en la expresión de match
.
En el lenguaje de Rust de Mozilla, los tipos de datos algebraicos y la coincidencia de patrones son conceptos importantes. La sintaxis también es bastante agradable. Considere el siguiente programa simple:
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);
}
Erlang tiene un sistema de tipo dinámico, por lo que no proporciona ninguna de las garantías que usted cita, pero el código de Erlang tiende a parecerse al producto de un sistema de tipo algebraico:
count([]) -> 0;
count([H|T]) -> 1 + count(T).
length({rect, X, Y}) -> math:sqrt(X*X + Y*Y);
length({polar, R, _A}) -> R.