Note initiale:
Cette question a été fermée après plusieurs modifications car je n'avais pas la terminologie appropriée pour énoncer avec précision ce que je cherchais. Sam Tobin-Hochstadt a ensuite posté un commentaire qui m'a fait reconnaître exactement ce que c'était: des langages de programmation qui prennent en charge les types d'intersection pour les valeurs de retour de fonction.
Maintenant que la question a été rouverte, j'ai décidé de l'améliorer en la réécrivant d'une manière (je l'espère) plus précise. Par conséquent, certaines réponses et commentaires ci-dessous peuvent ne plus avoir de sens car ils font référence à des modifications précédentes. (Veuillez consulter l'historique des modifications de la question dans de tels cas.)
Existe-t-il des langages de programmation populaires et fortement typés (tels que Haskell, Java générique, C #, F #, etc.) qui prennent en charge les types d'intersection pour les valeurs de retour de fonction? Si oui, lequel et comment?
(Si je suis honnête, j'aimerais vraiment voir quelqu'un montrer comment exprimer les types d'intersection dans un langage courant comme C # ou Java.)
Je vais donner un rapide exemple de ce à quoi pourraient ressembler les types d'intersection, en utilisant un pseudocode similaire à 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();
}
Autrement dit, la fonction fn
retourne une instance d'un certain type T
, dont l'appelant sait seulement qu'il implémente des interfaces IX
et IY
. (C'est-à-dire que, contrairement aux génériques, l'appelant ne peut pas choisir le type concret de T
- la fonction le fait. De cela, je suppose que ce T
n'est en fait pas un type universel, mais un type existentiel.)
PS: Je suis conscient que l'on pourrait simplement définir un interface IXY : IX, IY
et changer le type de retour de fn
en IXY
. Cependant, ce n'est pas vraiment la même chose, car souvent vous ne pouvez pas boulonner une interface supplémentaire IXY
à un type précédemment défini A
qui implémente uniquement IX
et IY
séparément.
Note de bas de page: Quelques ressources sur les types d'intersection:
L'article de Wikipedia pour "Type system" contient une sous - section sur les types d'intersection .
Rapport de Benjamin C. Pierce (1991), "Programmation avec les types d'intersection, les types d'union et le polymorphisme"
David P. Cunningham (2005), "Intersection types in practice" , qui contient une étude de cas sur le langage de Forsythe, qui est mentionnée dans l'article Wikipedia.
Une question Stack Overflow, "Union types and intersection types" qui a obtenu plusieurs bonnes réponses, dont celle-ci qui donne un exemple pseudocode de types d'intersection similaires au mien ci-dessus.
T
définit un type, même s'il est juste défini dans la déclaration de fonction comme "un type qui étend / implémenteIX
etIY
". Le fait que la réelle valeur de retour est un cas particulier de ce (A
ouB
respectivement) ne sont pas quelque chose de spécial ici, vous pourriez tout aussi bien y parvenir en utilisant auObject
lieu deT
.T
comme interfaceI
quand il implémente toutes les méthodes de l'interface, mais n'a pas déclaré cette interface".Réponses:
Scala a des types d'intersection complets intégrés dans la langue:
la source
En fait, la réponse évidente est: Java
Bien que cela puisse vous surprendre d'apprendre que Java prend en charge les types d'intersection ... il le fait en effet via l'opérateur lié de type "&". Par exemple:
Voir ce lien sur plusieurs types de bornes en Java, et aussi depuis l'API Java.
la source
<T extends IX & IY> T f() { if(condition) return new A(); else return new B(); }
. Et comment appelez-vous la fonction dans un tel cas? Ni A ni B ne peuvent apparaître sur le site d'appel, car vous ne savez pas lequel vous obtiendrez.Question d'origine posée pour "type ambigu". Pour cela, la réponse a été:
Type ambigu, évidemment aucun. L'appelant doit savoir ce qu'il obtiendra, donc ce n'est pas possible. Tout langage peut renvoyer soit le type de base, l'interface (éventuellement généré automatiquement comme dans le type d'intersection) ou le type dynamique (et le type dynamique est simplement un type avec des méthodes d'appel par nom, get et set).
Interface déduite:
Donc, fondamentalement, vous voulez qu'il retourne une interface
IXY
qui dériveIX
etIY
bien que cette interface n'ait pas été déclarée dans l'unA
ou l' autreB
, peut-être parce qu'elle n'a pas été déclarée lorsque ces types ont été définis. Dans ce cas:A
etB
ou le type d'intersection deIX
etIY
) lui-même.PS Un langage fortement typé est un langage dans lequel un objet de type donné ne peut pas être traité comme un objet d'un autre type, tandis qu'un langage faiblement typé est un langage qui a une distribution réinterprétée. Ainsi, tous les langages typés dynamiquement sont fortement typés , tandis que les langages faiblement typés sont l'assembly, C et C ++, les trois étant typés statiquement .
la source
Le type de langage de programmation Go a cela, mais uniquement pour les types d'interface.
Dans Go, tout type pour lequel les bonnes méthodes sont définies implémente automatiquement une interface, donc l'objection dans votre PS ne s'applique pas. En d'autres termes, créez simplement une interface qui a toutes les opérations des types d'interface à combiner (pour lesquelles il existe une syntaxe simple) et tout cela fonctionne correctement.
Un exemple:
la source
Vous pourriez être capable de faire ce que vous voulez en utilisant un type existentiel borné, qui peut être encodé dans n'importe quel langage avec des génériques et un polymorphisme borné, par exemple C #.
Le type de retour sera quelque chose comme (en code pseudo)
IAB = exists T. T where T : IA, IB
ou en C #:
Remarque: je n'ai pas testé cela.
Le fait est qu'il
IAB
doit être en mesure d'appliquer un IABFunc pour n'importe quel type de retourR
, et qu'ilIABFunc
doit être en mesure de travailler sur tous lesT
sous-typesIA
etIB
.L'intention de
DefaultIAB
est simplement d'envelopper un existantT
qui sousIA
- types etIB
. Notez que ceci est différent de votreIAB : IA, IB
dans quiDefaultIAB
peut toujours être ajouté à un existantT
plus tard.Les références:
la source
Apply
d'y être invoqué. Le gros problème est qu'il n'y a aucun moyen d'utiliser une fonction anonyme pour implémenter une interface, donc les constructions comme celle-ci finissent par être vraiment pénibles à utiliser.TypeScript est un autre langage typé qui prend en charge les types d'intersection
T & U
(ainsi que les types d'unionT | U
). Voici un exemple cité sur leur page de documentation sur les types avancés :la source
Ceylan a un support complet pour les types d' union et d' intersection de première classe .
Vous écrivez un type d'union comme
X | Y
et un type d'intersection commeX & Y
.Encore mieux, Ceylan présente de nombreux raisonnements sophistiqués sur ces types, notamment:
Consumer<X>&Consumer<Y>
est du même type queConsumer<X|Y>
siConsumer
est contravariant dansX
, etObject&Null
est le même type queNothing
, le type inférieur.la source
Les fonctions C ++ ont toutes un type de retour fixe, mais si elles renvoient des pointeurs, les pointeurs peuvent, avec des restrictions, pointer vers différents types.
Exemple:
Le comportement du pointeur renvoyé dépendra des
virtual
fonctions définies, et vous pouvez faire un downcast vérifié avec, disons,Base * foo = function(...);dynamic_cast<Derived1>(foo)
.C'est ainsi que le polymorphisme fonctionne en C ++.
la source
any
ou unvariant
type, comme le boost de modèles fournit. Ainsi, la restriction ne reste pas.class Base1{}; class Base2{}; class Derived1 : public Base1, public Base2 {}; class Derived2 : public Base1, public Base2 {}
... maintenant quel type pouvons-nous spécifier qui permet de retourner l'unDerived1
ou l'Derived2
autre niBase1
niBase2
directement?Python
C'est très, très fortement tapé.
Mais le type n'est pas déclaré lors de la création d'une fonction, les objets renvoyés sont donc "ambigus".
Dans votre question spécifique, un meilleur terme pourrait être "polymorphe". C'est le cas d'utilisation courant en Python qui consiste à renvoyer des types de variantes qui implémentent une interface commune.
Puisque Python est fortement typé, l'objet résultant sera une instance de
This
etThat
et ne peut pas (facilement) être contraint ou converti en un autre type d'objet.la source