Nota inicial:
Essa questão foi encerrada após várias edições, porque eu não tinha a terminologia adequada para indicar com precisão o que estava procurando. Sam Tobin-Hochstadt postou um comentário que me fez reconhecer exatamente o que era: linguagens de programação que suportam tipos de interseção para valores de retorno de função.
Agora que a pergunta foi reaberta, decidi melhorá-la reescrevendo-a de uma maneira (esperançosamente) mais precisa. Portanto, algumas respostas e comentários abaixo podem não fazer mais sentido, pois se referem a edições anteriores. (Consulte o histórico de edições da pergunta nesses casos.)
Existem linguagens de programação populares de tipo estatístico e fortemente tipado (como Haskell, Java genérico, C #, F # etc.) que suportam tipos de interseção para valores de retorno de função? Se sim, qual e como?
(Se eu for honesto, eu adoraria ver alguém demonstrar uma maneira de expressar tipos de interseção em uma linguagem convencional como C # ou Java.)
Vou dar um exemplo rápido de como os tipos de interseção podem parecer, usando algum pseudocódigo semelhante ao C #:
interface IX { … }
interface IY { … }
interface IB { … }
class A : IX, IY { … }
class B : IX, IY, IB { … }
T fn() where T : IX, IY
{
return … ? new A()
: new B();
}
Ou seja, a função fn
retorna uma instância de algum tipo T
, da qual o chamador sabe apenas que implementa interfaces IX
e IY
. (Ou seja, diferentemente dos genéricos, o chamador não escolhe o tipo concreto de T
- a função escolhe . A partir disso, eu suponho que, T
na verdade, não seja um tipo universal, mas existencial.)
PS: Estou ciente de que alguém poderia simplesmente definir ae interface IXY : IX, IY
alterar o tipo de retorno fn
para IXY
. No entanto, isso não é realmente a mesma coisa, porque geralmente você não pode anexar uma interface adicional IXY
a um tipo definido anteriormente A
que apenas implementa IX
e IY
separadamente.
Nota de rodapé: Alguns recursos sobre os tipos de interseção:
O artigo da Wikipedia para "Sistema de tipos" possui uma subseção sobre tipos de interseções .
Relatório de Benjamin C. Pierce (1991), "Programação com tipos de interseção, tipos de união e polimorfismo"
David P. Cunningham (2005), "Tipos de interseção na prática" , que contém um estudo de caso sobre o idioma de Forsythe, mencionado no artigo da Wikipedia.
Uma pergunta sobre estouro de pilha, "Tipos de união e tipos de interseção", que obteve várias boas respostas, entre elas uma que fornece um exemplo em pseudocódigo de tipos de interseção semelhantes aos meus acima.
T
define um tipo, mesmo que apenas seja definido na declaração da função como "algum tipo que estende / implementaIX
eIY
". O fato de o valor de retorno real ser um caso especial disso (A
ouB
respectivamente) não é nada de especial aqui, você também pode conseguir isso usando emObject
vez deT
.T
como interfaceI
quando implementa todos os métodos da interface, mas não declarou essa interface".Respostas:
O Scala possui tipos de interseção completos incorporados ao idioma:
fonte
De fato, a resposta óbvia é: Java
Embora você possa se surpreender ao saber que o Java suporta tipos de interseção ... ele realmente funciona através do operador vinculado ao tipo "&". Por exemplo:
Veja este link em vários limites de tipo em Java e também na API Java.
fonte
<T extends IX & IY> T f() { if(condition) return new A(); else return new B(); }
. E como você chama a função nesse caso? Nem A nem B podem aparecer no site da chamada, porque você não sabe qual deles receberá.Pergunta original solicitada "tipo ambíguo". Para isso a resposta foi:
Tipo ambíguo, obviamente nenhum. O chamador precisa saber o que receberá, por isso não é possível. Todo o idioma que você pode retornar é do tipo base, interface (possivelmente gerado automaticamente como no tipo de interseção) ou dinâmico (e o tipo dinâmico é basicamente apenas o tipo com métodos de chamada por nome, obtenção e configuração).
Interface inferida:
Então, basicamente, você deseja que ele retorne uma interface
IXY
que derivaIX
e,IY
embora essa interface não tenha sido declarada em umA
ou outroB
, possivelmente porque não foi declarada quando esses tipos foram definidos. Nesse caso:A
e /B
ou o tipo de interseçãoIX
eIY
).PS Uma linguagem fortemente tipada é aquela em que um objeto de um determinado tipo não pode ser tratado como objeto de outro tipo, enquanto a linguagem tipicamente fraca é aquela que tem uma conversão reinterpretada. Assim, todas as linguagens dinamicamente tipadas são fortemente tipadas , enquanto as linguagens fracamente tipadas são assembly, C e C ++, todas as três sendo tipadas estaticamente .
fonte
O Go Programming Language meio que tem isso, mas apenas para tipos de interface.
Em Ir, qualquer tipo para o qual os métodos corretos sejam definidos implementa automaticamente uma interface; portanto, a objeção no seu PS não se aplica. Em outras palavras, basta criar uma interface que tenha todas as operações dos tipos de interface a serem combinadas (para as quais existe uma sintaxe simples) e tudo isso apenas funcione.
Um exemplo:
fonte
Você pode fazer o que quiser usando um tipo existencial limitado, que pode ser codificado em qualquer idioma com genéricos e polimorfismo limitado, por exemplo, C #.
O tipo de retorno será algo como (no código psuedo)
IAB = exists T. T where T : IA, IB
ou em C #:
Nota: Eu não testei isso.
O ponto é que
IAB
deve ser capaz de aplicar um IABFunc para qualquer tipo de retornoR
e umIABFunc
deve ser capaz de trabalhar em qualquer um dosT
subtipos deIA
eIB
.A intenção de
DefaultIAB
é apenas agrupar um existenteT
que subtiposIA
eIB
. Observe que isso é diferente do seuIAB : IA, IB
queDefaultIAB
sempre pode ser adicionado a um existenteT
posteriormente.Referências:
fonte
Apply
que ele seja invocado. O grande problema é que não há como usar uma função anônima para implementar uma interface; portanto, construções como essa acabam sendo uma verdadeira dor de usar.TypeScript é outra linguagem digitada que suporta tipos de interseção
T & U
(junto com tipos de uniãoT | U
). Aqui está um exemplo citado em sua página de documentação sobre tipos avançados :fonte
O Ceilão tem suporte total para os tipos de união e interseção de primeira classe .
Você escreve um tipo de união como
X | Y
e um tipo de interseção comoX & Y
.Melhor ainda, o Ceilão apresenta muitos argumentos sofisticados sobre esses tipos, incluindo:
Consumer<X>&Consumer<Y>
é do mesmo tipoConsumer<X|Y>
queConsumer
é contravarianteX
eObject&Null
é do mesmo tipo queNothing
, do tipo inferior.fonte
Todas as funções do C ++ têm um tipo de retorno fixo, mas se retornarem ponteiros, os ponteiros poderão, com restrições, apontar para tipos diferentes.
Exemplo:
O comportamento do ponteiro retornado dependerá de quais
virtual
funções são definidas e você pode fazer um downcast verificado com, por exemplo,Base * foo = function(...);dynamic_cast<Derived1>(foo)
.É assim que o polimorfismo funciona em C ++.
fonte
any
ou umvariant
tipo, como o modelo fornece. Assim, a restrição não fica.class Base1{}; class Base2{}; class Derived1 : public Base1, public Base2 {}; class Derived2 : public Base1, public Base2 {}
... agora que tipo podemos especificar que permite o retorno querDerived1
ouDerived2
mas nemBase1
nemBase2
diretamente?Pitão
É muito, muito fortemente tipado.
Mas o tipo não é declarado quando uma função é criada; portanto, os objetos retornados são "ambíguos".
Na sua pergunta específica, um termo melhor pode ser "Polimórfico". Esse é o caso de uso comum no Python é retornar tipos de variantes que implementam uma interface comum.
Como o Python é fortemente digitado, o objeto resultante será uma instância de
This
ouThat
e não poderá (facilmente) ser coagido ou convertido para outro tipo de objeto.fonte