¿Existen lenguajes de programación que le permitan establecer aritmética en tipos?

9

Por curiosidad, ¿hay idiomas que le permitan establecer aritmética en tipos para crear nuevos tipos? Algo como:

interface A {
  void a();
  void b();
}

interface B {
  void b();
  void c();
}

interface C = A & B; // has b()
interface D = A | B; // has a(), b() and c()
interface E = (A & B) ^ B; // has c()

Sé que en algunos idiomas estas ideas se pueden expresar (es decir, Java tiene List<Comparable & Serializable>para la unión de las interfaces) pero nunca he oído hablar de un lenguaje que admita aritmética de tipos. ¡Gracias!

Marrón Haldean
fuente
77
¿Cómo sería útil tal mecanismo?
Robert Harvey
44
Un patrón que he visto mucho es una interfaz que extiende otras dos interfaces y no agrega nada (es decir, CanWriteAndCompare extends Serializable, Comparable {}) y estaba pensando en cómo generalizar esto.
Haldean Brown
2
Además, hoy me encontré con un caso en el que tengo un método que puede tomar una Ao una B, con dos implementaciones que se ven exactamente iguales. En el método, estoy llamando a un método polimórfico que puede tomar un Ao a B, por lo que las implementaciones son las mismas, pero como tengo que tomar dos tipos distintos, necesito dos implementaciones. Esto sería más fácil si pudiera hacerlo myMethod(A | B aOrB).
Haldean Brown
1
OrLa operación puede ser emulada por herencia múltiple.
usuario

Respuestas:

4

Tangent ( espec. 0.3 ) usa algo similar a esto. (descargo de responsabilidad: este es mi propio pequeño proyecto de investigación)

Actualmente withactúa como un operador de unión que modela la herencia, aunque el pragmatismo lo ha hecho no conmutativo. Una vez que introduce implementaciones a los métodos, una unión estricta de métodos con el mismo nombre a menudo no es lo que desea e imposible de hacer de todos modos.

intersectSe admite que los modelos tomen inferencia para algo así como foo(T,T)donde los parámetros son diferentes.

Los complementos fueron interesantes, pero dieron lugar a tipos parciales que no parecían tan útiles y / o problemáticos de incluir correctamente, por lo que no se incluyen.

Sé que hay algunos otros lenguajes de investigación que encontré que tenían algo similar, pero no puedo recordarlos en este momento. El problema principal es que las cosas no son realmente útiles sin la tipificación estructural, que no es muy popular en sí misma. El otro es que necesita algún tipo de tipo (tipo de tipos) para almacenar el tipo construido, o de lo contrario es solo una abreviatura de algo que no es particularmente idiomático sin esa capacidad. Y eso es mucho menos común que incluso la tipificación estructural.

Es parcial y no es mucho, pero ahí está.

Telastyn
fuente
5

Sí, Ceilán es un lenguaje con tipos de unión e intersección ad hoc, como se describe en este capítulo de la gira de Ceilán:

http://ceylon-lang.org/documentation/1.0/tour/types/

Es sorprendente la cantidad de expresiones geniales que obtienes de esto. Aquí hay un ejemplo interesante que escribí en un blog recientemente . Y aquí hay una breve presentación donde rápidamente paso por alto varios modismos simples .

Aún mejor, los tipos de unión / intersección son el "eslabón perdido" que hace que la inferencia de argumento de tipo genérico realmente funcione correctamente en Ceilán en comparación con otros lenguajes que combinan subtipo y polimorfismo paramétrico.

Tenga en cuenta que hay limitaciones para este tipo de "tipo aritmético", como lo ha descrito. Por ejemplo, no puede tener un operador de complemento establecido en el nivel de tipo, al menos no sin introducir indecidibilidad.

HTH

Gavin King
fuente
3

Scala lo admite parcialmente (intersecciones pero no uniones), y cualquier lenguaje con subtipo estructural (creo que OCaml es un ejemplo) o un sistema de tipos lo suficientemente potente como para emular que (Haskell es clásico) tendrá "tipos como conjuntos" completos "capacidades, al menos dentro del fragmento de su sistema de tipos que acepta tales cosas (relevante cuando se emula ala HList / OOHaskell).

Como no conozco muy bien OCaml, daré la parte de su ejemplo que funciona en Scala:

trait A {
  def a(): Unit
  def b(): Unit
}

abstract class B { // just to prove it works with both traits and classes
  def b(): Unit
  def c(): Unit
}

type C = A with B
type D = { def b(): Unit } // not an exact translation, because unions aren't directly available
// type `E` is unrepresentable

Una versión para Haskell dependería del sistema de registro que estaba utilizando, y probablemente sería algo torpe, porque sería emulada en lugar de ser compatible de forma nativa.

Hasta donde yo sé, Ceilán tiene tipos de intersección y unión integrados en el lenguaje con toda su potencia, por lo que presumiblemente podría codificar un "xor" de nivel de tipo en términos de esos.

Llama de Ptharien
fuente
1

Java admite intersecciones de tipos de interfaz en algunos contextos, aunque, por lo que puedo decir, no permite la creación de variables de tipos de intersección. Los tipos de intersección pueden entrar en juego, por ejemplo, cuando se usa el ? :operador. Si el segundo y tercer operandos a ese operador son interfaces no relacionadas que heredan de conjuntos de interfaces superpuestos, el resultado del operador será el conjunto de interfaces que son comunes a ambos.

Super gato
fuente