Fair Warning, je suis nouveau à la programmation fonctionnelle afin que je puisse tenir de nombreuses hypothèses mauvaises.
J'ai appris sur les types algébriques. De nombreux langages fonctionnels semblent en avoir, et ils sont assez utiles en conjonction avec la correspondance de modèles. Cependant, quel problème résolvent-ils réellement? Je peux implémenter un type algébrique apparemment (en quelque sorte) en C # comme ceci:
public abstract class Option { }
public class None : Option { }
public class Some<T> : Option
{
public T Value { get; set; }
}
var result = GetSomeValue();
if(result is None)
{
}
else
{
}
Mais je pense que la plupart seraient d'accord c'est un abâtardissement de la programmation orientée objet, vous ne devriez jamais faire. La programmation fonctionnelle ajoute-t-elle simplement une syntaxe plus propre qui rend ce style de programmation moins grossier? Que manque-t-il d'autre?
functional-programming
algebraic-data-type
ConditionRacer
la source
la source
class ThirdOption : Option{}
et vous donner cenew ThirdOption()
que vous attendiezSome
ouNone
?data Maybe a = Just a | Nothing
(équivalent àdata Option a = Some a | None
dans votre exemple): vous ne pouvez pas ajouter un troisième cas post-hoc. Alors que vous pouvez sorte de types sum Emuler en C # de la manière que vous avez montré, ce n'est pas la plus jolie.Réponses:
Les classes avec des interfaces et présentes héritage un monde ouvert: le monde peut ajouter un nouveau type de données. Pour une interface donnée, il peut y avoir des classes l'implémentant partout dans le monde, dans différents fichiers, dans différents projets, dans différentes entreprises. Ils le rendent facile à ajouter des cas aux structures de données, mais parce que les implémentations de l'interface est décentralisée, il est difficile d'ajouter une méthode à l'interface. Une fois que l'interface est publique, il est essentiellement gelé. On ne sait pas toutes les applications possibles.
Types de données algébriques sont les deux à cela, elles sont fermées . Tous les cas des données sont répertoriés en un seul endroit et non seulement les opérations peuvent répertorier les variantes de manière exhaustive, mais elles sont encouragées à le faire. Par conséquent, écrire une nouvelle fonction fonctionnant sur un type de données algébrique est trivial: il suffit d'écrire la putain de fonction. En retour, l'ajout de nouveaux cas est compliqué car vous devez parcourir la base de code entière et l'étendre tous
match
. Semblable à la situation avec les interfaces, dans la bibliothèque standard de Rust, l' ajout d'une nouvelle variante est un changement de rupture (pour les types publics).Ce sont les deux faces du problème d'expression . Types de données algébriques sont une solution incomplète, mais est donc POO. Les deux présentent des avantages en fonction du nombre de cas de données, de la fréquence à laquelle ces cas changent et de la fréquence à laquelle les opérations sont étendues ou modifiées. (C'est pourquoi de nombreux langages modernes proposent les deux, ou quelque chose de similaire, ou vont directement vers des mécanismes plus puissants et plus compliqués qui tentent de subsumer les deux approches.)
la source
C'est une simplification peut-être, mais oui.
Soyons clairs sur les types de données algébriques (résumant ce lien fin de Learn you as Haskell):
votre exemple ne fonctionne vraiment avec la première.
C # a struct, les classes, les énumérations, les génériques sur le dessus, et ceux des tas de règles régissant la manière dont ces comportements sont.
Combinés à une certaine syntaxe pour aider, les langages fonctionnels peuvent décomposer les opérations jusqu'à ces deux chemins, offrant une approche propre, simple et élégante des types.
Ils résolvent le même problème que tout autre système de type: « Quelles sont les valeurs légal d'utiliser ici? » - ils prennent juste une approche différente.
la source
Cela peut vous surprendre d'apprendre que pattern matching est pas considéré comme le meilleur moyen idiomatiques de travailler avec les options. Voir la documentation des options de Scala pour plus à ce sujet.
La plupart du temps ce que vous manquez est il y a un tas de fonctions créées pour rendre le travail plus facile avec options. Prenons l'exemple principal de la documentation Scala:
Remarquez comment
map
etfilter
laissez vous enchaînez les opérations sur les options, sans avoir à vérifier chaque point si vous avezNone
ou non. Puis à la fin, vous pouvez utilisergetOrElse
pour spécifier une valeur par défaut. A aucun moment que tu fais quelque chose de « brut » comme types de vérification. Haskell a son propre ensemble de fonctions analogiques, sans parler du grand ensemble de fonctions qui fonctionnera sur n'importe quelle monade ou foncteur.De même, lorsque vous créez vos propres types, vous êtes censé fournir des fonctions similaires à travailler avec eux.
la source