Existem linguagens de programação que permitem definir aritmética nos tipos?

9

Por curiosidade, existem idiomas que permitem definir aritmética nos tipos para criar novos 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()

Eu sei que em algumas linguagens essas idéias podem ser expressas (isto é, Java possui List<Comparable & Serializable>para a união das interfaces), mas nunca ouvi falar de uma linguagem que suporte aritmética de tipo. Obrigado!

Castanho Haldean
fonte
7
Como esse mecanismo seria útil?
21412 Robert Harvey
4
Um padrão que eu já vi muito é uma interface que estende duas outras interfaces e não adiciona nada (isto é, CanWriteAndCompare extends Serializable, Comparable {}) e eu estava pensando em como generalizar isso.
Caldeus Brown
2
Além disso, encontrei um caso hoje em que tenho um método que pode levar um Aou um B, com duas implementações que parecem exatamente iguais. No método, estou chamando um método polimórfico que pode levar um Aou um B, de modo que as implementações são as mesmas, mas como tenho que usar dois tipos distintos, preciso de duas implementações. Isso seria mais fácil se eu pudesse fazer myMethod(A | B aOrB).
Caldeus Brown
11
OrA operação pode ser emulada por herança múltipla.
User

Respostas:

4

Tangent ( 0.3 spec ) usa algo parecido com isso. (aviso: este é meu pequeno projeto de pesquisa)

Atualmente, withatua como um operador de união que modela a herança, embora o pragmatismo a torne não comutativa. Depois de introduzir implementações nos métodos, uma união estrita de métodos com o mesmo nome geralmente não é o que você deseja e é impossível fazer o que é certo.

intersectÉ suportado que os modelos digitem inferência para algo como foo(T,T)onde os parâmetros são diferentes.

Os complementos eram interessantes, mas levavam a tipos parciais que não pareciam tão úteis e / ou problemáticos para serem incluídos corretamente - portanto, não foram incluídos.

Eu sei que existem algumas outras linguagens de pesquisa que encontrei com algo semelhante, mas não consigo lembrar delas no momento. A questão principal é que as coisas não são realmente úteis sem a tipagem estrutural, o que não é muito popular em si. A outra é que você precisa de algum tipo (tipo de tipo) para armazenar o tipo construído, ou então é apenas uma abreviação de algo que não é particularmente idiomático sem esse recurso. E isso é muito menos comum do que a digitação estrutural.

É tendencioso, e não é muito, mas existe.

Telastyn
fonte
5

Sim, o Ceilão é um idioma com união ad hoc e tipos de interseção, conforme descrito neste capítulo do tour pelo Ceilão:

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

É incrível o número de expressões legais que você obtém disso. Aqui está um exemplo interessante que eu escrevi recentemente . E aqui está uma breve apresentação em que passo rapidamente por vários idiomas simples .

Melhor ainda, os tipos de união / interseção são o "elo perdido" que faz com que a inferência de argumento de tipo genérico realmente funcione corretamente no Ceilão, em oposição a outros idiomas que combinam subtipo e polimorfismo paramétrico.

Observe que existem limitações para esse tipo de "tipo aritmético", como você o descreveu. Por exemplo, você não pode ter um operador de complemento definido no nível de tipo, pelo menos não sem introduzir indecisão.

HTH

Gavin King
fonte
3

O Scala o suporta parcialmente (interseções, mas não uniões), e qualquer linguagem com subtipo estrutural (acho que OCaml é um exemplo) ou um sistema de tipos poderoso o suficiente para imitar que (Haskell é um clássico) terá "tipos como conjuntos completos" "capacidades, pelo menos dentro do fragmento de seu sistema de tipos que aceita essas coisas (relevante quando é emulado ala HList / OOHaskell).

Como não conheço muito bem o OCaml, darei a parte do seu exemplo que funciona no 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

Uma versão para Haskell dependeria do sistema de registro que você estava usando e provavelmente seria um pouco desajeitada, porque seria emulada e não suportada nativamente.

Tanto quanto eu sei, o Ceilão tem tipos de interseção e união incorporados ao idioma com força total, portanto é possível presumir codificar um "xor" no nível de tipo em termos deles.

Chama de Ptharien
fonte
1

Java suporta interseções de tipos de interface em alguns contextos, embora isso não permita, até onde posso dizer, permitir a criação de variáveis ​​de tipos de interseção. Os tipos de interseção podem entrar em jogo, por exemplo, ao usar o ? :operador. Se o segundo e o terceiro operandos para esse operador são interfaces não relacionadas que herdam de conjuntos de interfaces sobrepostos, o resultado do operador será o conjunto de interfaces comuns a ambos.

supercat
fonte