Dans Haskell, pourquoi n’y a-t-il pas un TypeClass pour les choses qui peuvent agir comme des listes?

Je lis Apprendre un Haskell et je me demande pourquoi tant de choses agissent comme une liste, et rien dans le Prélude n’utilise la fonctionnalité native des classes de type pour configurer cela:

“La version de bytessortingng de: est appelée contre. Elle prend un octet et une bytessortingng et place l’octet au début. Elle est paresseuse, donc elle va créer un nouveau morceau même si le premier morceau dans le bytessortingng n’est pas plein. il vaut mieux utiliser la version ssortingcte des inconvénients, par contre si vous allez insérer beaucoup d’octets au début d’un bytessortingng. ”

Pourquoi n’y a-t-il pas un listable TypeClass ou quelque chose qui offre la fonction : pour unifier Data.ByteSsortingng , Data.List , Data.ByteSsortingng.Lazy , etc.? Y a-t-il une raison à cela ou est-ce juste un élément de l’inheritance Haskell? En utilisant : comme exemple est un peu un euphémisme, également de LYAH:

Sinon, les modules de bytessortingng ont une charge de fonctions analogues à celles de Data.List, y compris, mais sans s’y limiter, head, tail, init, null, length, map, reverse, foldl, foldr, concat, takeWhile, filter , etc.

Le package ListLike semble fournir ce que vous recherchez. Je n’ai jamais compris pourquoi ce n’est pas plus populaire.

En dehors de ListLike, l’une des raisons pour lesquelles ce n’est pas implémenté dans le Prélude est qu’il n’est pas possible de le faire sans invoquer certaines extensions de langage (classes de type multi-param et fundeps ou types associés). Il existe trois sortes de conteneurs à prendre en compte:

  1. Conteneurs qui ne se soucient pas du tout de leurs éléments (par exemple, [])
  2. Conteneurs uniquement implémentés pour des éléments spécifiques (ex: bytessortingngs)
  3. Conteneurs polymorphes sur des éléments mais nécessitant un contexte (par exemple Data.Vector.Storable, qui contiendra n’importe quel type avec une instance stockable).

Voici une classe très simple de style ListLike sans utiliser aucune extension:

 class Listable container where head :: container a -> a instance Listable [] where head (x:xs) = x instance Listable ByteSsortingng where --comstackr error, wrong kind instance Listable SV.Vector where head v = SV.head --comstackr error, can't deduce context (Storable a) 

Ici le container a du genre *->* . Cela ne fonctionnera pas pour les bytessortingngs car ils ne permettent pas un type arbitraire; ils ont du genre * . Cela ne fonctionnera pas non plus pour un vecteur Data.Vector.Strable, car la classe n’inclut pas le contexte (la contrainte Storable).

Vous pouvez résoudre ce problème en changeant votre définition de classe en

 class ListableMPTC container elem | container -> elem where 

ou

 class ListableAT container where type Elem container :: * 

Maintenant, le container a du type * ; c’est un constructeur de type entièrement appliqué. C’est-à-dire que vos instances ressemblent à

 instance ListableMPTC [a] a where 

mais vous n’êtes plus Haskell98.

C’est pourquoi même une simple interface de type Listable est non sortingviale; cela devient un peu plus difficile lorsque vous devez tenir compte de différentes sémantiques de collection (par exemple, les files d’attente). L’autre grand défi concerne les données mutables et immuables. Jusqu’à présent, toutes les tentatives que j’ai faites (sauf une) ont abouti à un problème en créant une interface mutable et une interface immuable. La seule interface que je connaisse qui unifiait les deux était époustouflante, invoquait un tas d’extensions et avait des performances assez médiocres.

Addendum: bytessortingngs

Totalement conjecture de ma part, mais je pense que nous sums coincés avec les sous-tests en tant que produit de l’évolution. C’est-à-dire qu’ils constituaient la première solution pour les opérations d’E / S à faible performance, et il était logique d’utiliser Ptr Word8 pour l’interfaçage avec les appels du système d’E / S. Les opérations sur les pointeurs nécessitent Storable, et les extensions nécessaires (comme décrit ci-dessus) pour faire fonctionner le polymorphism n’étaient probablement pas disponibles. Maintenant, il est difficile de surmonter leur élan. Un conteneur similaire avec polymorphism est certainement possible, le package storablevector implémente ceci, mais il n’est pas aussi populaire.

Les bytessortingngs pourraient-ils être polymorphes sans aucune ressortingction sur les éléments? Je pense que le plus proche Haskell est le type Array. Ce n’est pas aussi bon qu’une chaîne d’octets pour les E / S de bas niveau car les données doivent être décompressées du pointeur au format interne du tableau. En outre, les données sont encadrées, ce qui ajoute un surcroît d’espace considérable. Si vous voulez un stockage sans emballage (moins d’espace) et une interface efficace avec C, les pointeurs sont la voie à suivre. Une fois que vous avez un Ptr, vous avez besoin de Storable, et vous devez ensuite inclure le type d’élément dans la classe de type, vous devez donc avoir des extensions.

Cela étant dit, je pense qu’avec les extensions appropriées disponibles, c’est essentiellement un problème résolu pour toute implémentation de conteneur unique (API modulo mutable / immuable). La partie la plus difficile consiste maintenant à proposer un ensemble de classes judicieuses, utilisables pour différents types de structures (listes, tableaux, files d’attente, etc.) et suffisamment flexibles pour être utiles. Personnellement, je m’attendrais à ce que ce soit relativement simple, mais je peux me tromper.

Le principal problème d’une telle classe est que même si elle existait, elle ne présenterait qu’une similitude superficielle.

L’ asymptotique du même algorithme construit en utilisant différentes structures varierait énormément.

Dans le cas de stades courts, il est terrible de les construire avec des inconvénients, car vous finissez par copier toute la chaîne à chaque fois que vous ajoutez un autre caractère. Cette opération O (1) sur une liste en fait une opération O (n) sur un Bytessortingng.

Cela conduit à un comportement O (n ^ 2) lorsque vous implémentez le premier algorithme qui pourrait vous venir à l’esprit, la carte, alors que construire une liste ou Data.Sequence.Seq avec des cons pour les bytessortingngs ou les vecteurs aussi avec un peu de reflection.

Il s’avère que l’utilité d’une telle classe à la lumière de cela est plus superficielle que réelle.

Je ne dis pas qu’une bonne conception ne peut pas être trouvée, mais une telle conception serait difficile à utiliser et à optimiser et une version utilisable de la conception ne finirait probablement pas par être Haskell 98.

J’ai extrait certaines parties de cet espace de conception dans mon paquetage de clés, qui fournit de nombreuses fonctions d’indexation dans des conteneurs, etc., mais j’ai délibérément évité de fournir une API de type liste a.) peu de succès et b.) en raison des préoccupations asymptotiques ci-dessus.

tl; dr Vous voulez généralement implémenter des algorithmes très différemment lorsque les asymptotiques des opérations sous-jacentes changent.

qui offre la fonction pour unifier Data.ByteSsortingng, Data.List, Data.ByteSsortingng.Lazy, etc.

Il y a eu des tentatives pour arriver à une bonne interface de séquence, et b) une interface de conteneur, cependant, l’unification de types de données de types différents, avec des contraintes de type différentes, a généralement rendu les résultats suffisamment inhabituels qu’il est difficile d’imaginer les mettre dans la bibliothèque de base. De même pour les tableaux, bien que le package Vector ait maintenant une interface assez générale (basée sur les types de données associés).

Il y a quelques projets pour unifier ces différents types de données semi-liés avec une seule interface, donc j’espère que nous verrons bientôt un résultat. De même pour les types de conteneur. Le résultat ne sera pas sortingvial si.

Il y a deux classes de types appelées Foldable et Traversable qui visent à abstraire certains comportements courants1 des listes et autres structures de données séquentielles. Cependant, toutes les structures de données ne disposent pas d’instances de ce type, et je ne sais pas si elles sont suffisamment transparentes pour le compilateur de manière à pouvoir toujours les optimiser (est-ce que quelqu’un en sait quelque chose?)

Source: Pliable et Traversable
Voir aussi cette réponse à Pourquoi Haskell manque-t-il des classes de caractères «évidentes»?

ByteSsortingng n’est pas un type générique.

Dans d’autres langages, il y a quelque chose comme Sequence pour toutes les structures de données de type liste. Je pense que cela fonctionne, avec des extensions correctes:

 class Seq ab | a -> b where head :: a -> b isTail :: a -> Bool # ([a]) is a sequence of a's instance Seq [a] a where head (x:xs) = x isTail = (== []) # ByteSsortingng is a sequence of chars instance Seq ByteSsortingng Char 

Ou essayez ceci?

 type BS a = ByteSsortingng instance List BS 

Il n’y a pas beaucoup de valeur à avoir une classe de type pour les données de type liste dans Haskell. Pourquoi? À cause de la paresse Vous pouvez simplement écrire une fonction qui convertit vos données en une liste, puis utiliser cette liste. La liste ne sera construite que lorsque ses sous-listes et ses éléments seront demandés, et leur mémoire sera éligible à la collecte dès qu’il ne rest aucune référence aux préfixes.

Il existe une valeur pour une classe de type qui fournit une fonction toList générique, mais qui existe déjà dans Data.Foldable .

Donc, fondamentalement, la solution consiste à implémenter Data.Foldable et à utiliser sa fonction toList .