J'essaie de comparer les classes de types de Haskell et les interfaces de C #. Supposons qu'il y ait un Functor
.
Haskell:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Comment implémenter cette classe de type comme interface en C #?
Ce que j'ai essayé:
interface Functor<A, B>
{
F<B> fmap(Func<A, B> f, F<A> x);
}
Ceci est une implémentation non valide et je suis en fait coincé avec un générique F
type qui devrait être renvoyé par fmap
. Comment le définir et où?
Est-il impossible d'implémenter Functor
en C # et pourquoi? Ou peut-être existe-t-il une autre approche?
Réponses:
Le système de type de C # manque de quelques fonctionnalités nécessaires pour implémenter correctement les classes de type en tant qu'interface.
Commençons par votre exemple, mais la clé montre un compte rendu plus complet de ce qu'est et fait une classe de types, puis essaie de les mapper en bits C #.
Il s'agit de la définition de classe de type, ou similaire à l'interface. Examinons maintenant la définition d'un type et son implémentation de cette classe de type.
Maintenant, nous pouvons voir très clairement un fait distinct des classes de type que vous ne pouvez pas avoir avec des interfaces. L'implémentation de la classe type ne fait pas partie de la définition du type. En C #, pour implémenter une interface, vous devez l'implémenter dans le cadre de la définition du type qui l'implémente. Cela signifie que vous ne pouvez pas implémenter une interface pour un type que vous n'implémentez pas vous-même, mais dans Haskell, vous pouvez implémenter une classe de type pour tout type auquel vous avez accès.
C'est probablement le plus grand immédiatement, mais il y a une autre différence assez importante qui fait que l'équivalent C # ne fonctionne vraiment pas aussi bien, et vous le touchez dans votre question. Il s'agit du polymorphisme. Il y a aussi des choses relativement génériques que Haskell vous permet de faire avec des classes de types qui ne se traduisent pas, surtout lorsque vous commencez à regarder la quantité de générique dans les types existentiels ou d'autres extensions GHC comme les ADT génériques.
Vous voyez, avec Haskell vous pouvez définir les foncteurs
Ensuite, dans la consommation, vous pouvez avoir une fonction:
C'est ici que se trouve le problème. En C # comment écrivez-vous cette fonction?
Il y a donc quelques problèmes avec la version C #, d'une part je ne suis même pas certain que cela vous permettra d'utiliser le
<b>
qualificatif comme je l'ai fait là-bas, mais sans cela, je suis certain qu'il ne serait pas distribuéShow<>
correctement (n'hésitez pas à essayer et compiler pour le découvrir, je ne l'ai pas).Le plus gros problème ici est cependant que contrairement à Haskell où nous avions
Terminal
défini nos s comme faisant partie du type, puis utilisables à la place du type, en raison du manque de polymorphisme paramétrique approprié de C # (qui devient super évident dès que vous essayez d'interopérer F # avec C #), vous ne pouvez pas distinguer clairement ou proprement si Droite ou Gauche sont desTerminal
s. Le mieux que vous puissiez faire est d'utilisernull
, mais que faire si vous essayez de faire une valeur de type aFunctor
ou dans le casEither
où vous distinguez deux types qui ont tous deux une valeur? Maintenant, vous devez utiliser un type et avoir deux valeurs différentes pour vérifier et basculer entre pour modéliser votre discrimination?L'absence de types de somme appropriés, de types d'union, d'ADT, quel que soit le nom que vous voulez leur donner, fait vraiment perdre beaucoup de ce que les classes de types vous donnent parce qu'en fin de compte, elles vous permettent de traiter plusieurs types (constructeurs) comme un seul type, et le système de type sous-jacent de .NET n'a tout simplement pas un tel concept.
la source
Vous avez besoin de deux classes, une pour modéliser le générique d'ordre supérieur (le foncteur) et l'autre pour modéliser le foncteur combiné avec la valeur libre A
Donc, si nous utilisons l'option monade (car toutes les monades sont des foncteurs)
Vous pouvez ensuite utiliser des méthodes d'extension statiques pour convertir de IF <Option, B> en Some <A> lorsque vous devez
la source
pure
l'interface générique du foncteur: le compilateur se plaintIF<Functor, A> pure<A>(A a);
de "Le typeFunctor
ne peut pas être utilisé comme paramètre de typeFunctor
dans le type de méthode génériqueIF<Functor, A>
. Il n'y a pas de conversion de boxe ni de conversion de paramètre de type deFunctor
àF<Functor>
". Qu'est-ce que ça veut dire? Et pourquoi faut-il définirpure
en deux endroits? De plus, ne devrait paspure
être statique?