Je veux construire une liste de différentes choses qui ont une propriété en commun, à savoir qu'elles peuvent être transformées en chaîne. L'approche orientée objet est simple : définir l'interface Showable
et faire en sorte que les classes d'intérêt l'implémentent. Le deuxième point peut en principe poser un problème lorsque vous ne pouvez pas modifier les classes, mais supposons que ce ne soit pas le cas. Dans ce cas, vous créez une liste de Showable
et le remplir avec des objets de ces classes sans aucun bruit supplémentaire (par exemple, l'upcasting est généralement fait implicitement). Preuve de concept en Java est donné ici .
Ma question est la suivante : quelles sont les options dont je dispose pour cela en Haskell ? Je discute ci-dessous des approches que j'ai essayées et qui ne me satisfont pas vraiment.
Approche 1 : existentielles. Fonctionne mais c'est moche.
{-# LANGUAGE ExistentialQuantification #-}
data Showable = forall a. Show a => Sh a
aList :: [Showable]
aList = [Sh (1 :: Int), Sh "abc"]
Le principal inconvénient pour moi ici est la nécessité de Sh
lors du remplissage de la liste. Cela ressemble beaucoup aux opérations d'upcast qui sont implicitement effectuées dans les langages OO.
Plus généralement, le wrapper factice Showable
pour la chose qui est déjà dans la langue - Show
classe de type - ajoute du bruit supplémentaire dans mon code. Pas bon.
Approche 2 : impredicatifs. Désiré mais ne fonctionne pas.
Le type de liste le plus simple pour moi, et ce que je désire vraiment, serait le suivant :
{-# LANGUAGE ImpredicativeTypes #-}
aList :: [forall a. Show a => a]
aList = [(1 :: Int), "abc"]
En plus de cela ( comme j'ai entendu ) ImpredicativeTypes
est "fragile au mieux et cassé au pire". il ne compile pas :
Couldn't match expected type ‘a’ with actual type ‘Int’
‘a’ is a rigid type variable bound by
a type expected by the context: Show a => a
et la même erreur pour "abc"
. (Note : signature de type pour 1 : sans elle, je reçois un message encore plus bizarre : Could not deduce (Num a) arising from the literal ‘1’
).
Approche 3 : Les types Rank-N avec une sorte de listes fonctionnelles (listes de différences ?).
Au lieu de problèmes ImpredicativeTypes
on préfèrerait probablement un système plus stable et plus largement accepté. RankNTypes
. Cela signifie essentiellement : déplacer le site souhaité forall a. Show a => a
constructeur hors type (c'est-à-dire []
) aux types de fonctions simples. Par conséquent, nous avons besoin d'une représentation des listes en tant que fonctions ordinaires. Comme je l'ai à peine entendu, il existe de telles représentations. La seule dont j'ai entendu parler est celle des listes de différences. Mais en Dlist
paquet le type principal est le bon vieux data
donc nous revenons aux imprédicatifs. Je n'ai pas approfondi cette ligne car je soupçonne qu'elle pourrait produire un code plus verbeux que dans l'approche 1. Mais si vous pensez que ce n'est pas le cas, veuillez me donner un exemple.
Ligne de fond Comment attaquer une telle tâche en Haskell ? Pourriez-vous donner une solution plus succincte qu'en langage OO (notamment au lieu de remplir une liste - voir le commentaire pour le code dans l'approche 1) ? Pouvez-vous commenter la pertinence des approches listées ci-dessus ?
UPD (sur la base des premiers commentaires) : la question est bien sûr simplifiée pour des raisons de lisibilité. Le vrai problème est plutôt de savoir comment stocker des choses qui partagent la même classe de type, c'est-à-dire qui peuvent être traitées ultérieurement de plusieurs façons ( Show
n'a qu'une seule méthode, mais les autres classes peuvent en avoir plusieurs). Cela écarte les solutions qui suggèrent d'appliquer show
au moment de remplir une liste.
20 votes
Il y a une différence essentielle entre Java et Haskell qui vous échappe. En Java, l'information sur le type est pas perdu lors de l'upcasting, ce qui vous permet de downcasting à nouveau et de faire d'autres choses non utiles.
String
y plus tard (à condition de choisir le bon type pour le downcast). En Haskell, une fois que vous avez fait un upcast, l'information sur le type est perdu et vous ne pouvez plus vous baisser. Donc vous pourriez tout aussi bien avoir une liste deString
s. Voir aussiDynamic
qui offre une interface plus riche que "simplement".String
".0 votes
@DanielWagner pas vraiment sûr que le downcasting soit lié au sujet. Je n'ai besoin que d'upcasting. Oui, je peux transformer cela en une liste de
String
comme mentionné dans la réponse de @ErikR, mais la question porte davantage sur la lisibilité : répétitif.show
n'a pas l'air bien pour moi.1 votes
Il n'est pas toujours facile de dire si le downcasting est lié ou non au sujet traité -- les gens l'incluent souvent dans leurs hypothèses au point d'oublier de le mentionner. (Ce n'est pas la première fois que ce sujet est abordé, ni ici ni sur IRC ;-) Quant à "ça ne me semble pas bon", eh bien, il n'y a pas de comptabilité pour le goût. Si vous pouvez donner une plainte objective et technique, nous pourrons peut-être discuter de la mesure dans laquelle elle peut être traitée.
31 votes
Je n'aime pas beaucoup le titre de cette question. Il prétend qu'il s'agit d'un conflit entre des langues/paradigmes, appelant éventuellement les gens d'un côté à se battre dans une guerre des langues.
9 votes
Il s'agit d'un anti-modèle connu, également abordé dans le document Le blog de Luke Palmer . Bien que je ne sois pas tout à fait d'accord avec l'ensemble de l'article du blog, je pense que l'argument de base qu'il avance est solide.
0 votes
@ArtemPelenitsyn Vous dites que le vrai problème est plutôt de savoir comment stocker des choses qui partagent la même classe de type qui peut avoir plus d'une méthode. Donnez-nous un exemple. Nous ne pouvons pas vous donner une réponse précise tant que vous ne nous donnez pas une question précise.
11 votes
Dès le titre " Hot Network Questions-optimized ", je suis arrivé prêt à voter pour fermer comme opinion. J'ai été agréablement surpris par la qualité de la question, mais une fois vos 15 minutes de HNQ terminées, essayez de changer le titre pour quelque chose de moins susceptible d'attirer de mauvaises réponses.
1 votes
Cela peut-il fonctionner en général ? Par exemple, pour le
Eq
Par exemple, ne faudrait-il pas permettre la comparaison de différents types, ce qui devrait être impossible du point de vue du type ?2 votes
Pour ce que ça vaut, votre deuxième tentative (avec des types d'impédance) n'est pas la même que la première et n'est certainement pas ce que vous voulez. A
[forall a. Show a => a]
est une liste d'habitants deforall a. Show a => a
. Il n'existe qu'un seul habitant de ce type : le fond. Il n'y a pas d'autre valeur possible qui puisse devenir toutShow
que l'appelant demande. Aucun Int ne peut habiter ce type, aucun String... rien. C'est littéralement l'opposé de ce que vous vouliez à l'origine. Vous vouliez probablement[exists a. Show a => a]
ou, skolémisé,[(forall a. Show a => a -> r) -> r]
.