Retour considéré comme dangereux? Le code peut-il être fonctionnel sans cela?

45

OK, donc le titre est un peu clickbaity mais sérieusement je suis sur un tell, ne demande pas un coup de pied pendant un moment. J'aime la façon dont cela encourage les méthodes à être utilisées comme messages de manière réellement orientée objet. Mais cela pose un problème récurrent qui me trotte dans la tête.

Je suis venu pour suspecter qu'un code bien écrit peut suivre les principes d'OO et les principes fonctionnels en même temps. J'essaie de réconcilier ces idées et le gros problème que j'ai rencontré est le suivant return.

Une fonction pure a deux qualités:

  1. L'appeler de manière répétée avec les mêmes entrées donne toujours le même résultat. Cela implique que c'est immuable. Son état n'est défini qu'une fois.

  2. Il ne produit aucun effet secondaire. Le seul changement causé en l'appelant produit le résultat.

Alors, comment peut-on être purement fonctionnel si vous avez juré d'utiliser returnvotre méthode de communication des résultats?

Le tell, ne demande pas idée fonctionne en utilisant ce que certains considèrent un effet secondaire. Lorsque je traite un objet, je ne lui pose pas de question sur son état interne. Je lui dis ce que je dois faire et il utilise son état interne pour comprendre quoi faire avec ce que je lui ai dit de faire. Une fois que je le dis, je ne demande pas ce que ça a fait. Je m'attends juste à ce qu'il ait fait quelque chose à propos de ce qu'il a été dit de faire.

Je pense à Tell, Don't Ask, à plus qu’un nom différent pour l’encapsulation. Quand j'utilise returnje n'ai aucune idée de ce qui m'a appelé. Je ne peux pas parler de protocole, je dois le forcer à gérer mon protocole. Ce qui, dans de nombreux cas, est exprimé par l'état interne. Même si ce qui est exposé n’est pas exactement l’état, c’est généralement juste quelques calculs effectués sur des arguments d’état et d’entrée. Le fait de disposer d'une interface permettant de répondre offre la possibilité de transformer les résultats en quelque chose de plus significatif que l'état ou les calculs internes. C'est un message qui passe . Voir cet exemple .

Il y a bien longtemps, lorsque les disques durs contenaient des disques et qu'une clé USB correspondait à ce que vous faisiez dans la voiture lorsque le volant était trop froid pour être touché, on m'a appris à quel point les personnes agaçantes considèrent les fonctions sans paramètres. void swap(int *first, int *second)semblait si pratique, mais nous avons été encouragés à écrire des fonctions renvoyant les résultats. J'ai donc pris cela à cœur avec foi et j'ai commencé à le suivre.

Mais maintenant, je vois des gens qui construisent des architectures où les objets laissent leur fabrication contrôler le lieu où ils envoient leurs résultats. Voici un exemple d'implémentation . L'injection de l'objet de port de sortie ressemble un peu à l'idée de paramètre out. Mais c'est comme ça que les objets qui disent ne pas demander disent aux autres objets ce qu'ils ont fait.

Lorsque j'ai découvert les effets secondaires, j'ai tout de suite pensé au paramètre de sortie. On nous disait de ne pas surprendre les gens en leur faisant effectuer une partie du travail de manière surprenante, c'est-à-dire en ne suivant pas la return resultconvention. Maintenant, bien sûr, je sais qu’il existe une pile de problèmes de threads asynchrones parallèles qui entraînent des effets secondaires, mais le retour n’est en réalité qu’une convention selon laquelle vous laissez le résultat affiché sur la pile afin que tout ce qui est appelé puisse être supprimé ultérieurement. C'est tout ce que c'est vraiment.

Ce que j'essaie vraiment de demander:

Est-ce returnle seul moyen d'éviter toute cette misère d'effets secondaires et d'obtenir la sécurité du fil sans serrures, etc. Ou puis-je suivre , ne pas demander de manière purement fonctionnelle?

confits_orange
la source
3
Si vous choisissez de ne pas ignorer la séparation des requêtes de commande, considérez-vous que votre problème est résolu?
Rwong
30
Réfléchissez au fait que de vous lancer sur une lancée pourrait indiquer que vous vous engagez dans une conception axée sur le dogme plutôt que de raisonner le pour et le contre de chaque situation spécifique.
Blrfl
3
En général, quand les gens parlent de «retour» comme étant nuisible, ils disent que cela va à la programmation structurée, non fonctionnelle, et à une seule déclaration de retour à la fin de la routine (et peut - être à la fin des deux côtés d’un bloc if / else est lui-même le dernier élément) n’est pas inclus dans cela.
Random832
16
@jameslarge: fausse dichotomie. Ne pas se laisser entraîner aux conceptions par la pensée dogmatique n’est pas la même chose que le Far West / tout va comme vous le dites. Le but est de ne pas permettre au dogme de faire obstacle à un code bon, simple et évident. La loi universelle est pour les débiles; le contexte est pour les rois.
Nicol Bolas
4
Il y a une chose pour moi qui n'est pas claire dans votre question: vous dites que vous avez juré d'utiliser 'retour', mais pour autant que je sache, n'indiquez pas explicitement cela à vos autres concepts, ni ne dites pourquoi vous l'avez fait. Lorsque vous le combinez avec la définition d'une fonction pure, y compris la production d'un résultat, vous créez un problème impossible à résoudre.
Danikov

Réponses:

96

Si une fonction n'a aucun effet secondaire et ne renvoie rien, alors la fonction est inutile. C'est aussi simple que ça.

Mais je suppose que vous pouvez utiliser certaines astuces si vous souhaitez suivre la lettre des règles et ignorer le raisonnement sous-jacent. Par exemple, utiliser un paramètre de sortie revient à ne pas utiliser de retour à proprement parler. Mais cela fait toujours exactement la même chose qu’un retour, mais d’une manière plus alambiquée. Donc, si vous croyez que le retour est mauvais pour une raison , utiliser un paramètre out est clairement mauvais pour les mêmes raisons sous-jacentes.

Vous pouvez utiliser des astuces plus compliquées. Par exemple, Haskell est célèbre pour le tour de la monade IO où vous pouvez avoir des effets secondaires dans la pratique, sans pour autant avoir d'effets secondaires d'un point de vue théorique. La poursuite du style est un autre truc, qui vous permet d’éviter les retours au prix de transformer votre code en spaghetti.

En fin de compte, sans astuces idiotes, les deux principes des fonctions sans effets secondaires et du "non-retour" ne sont tout simplement pas compatibles. En outre, je soulignerai que les deux principes sont vraiment mauvais (les dogmes en réalité), mais la discussion est différente.

Des règles telles que «Dites, ne demandez pas» ou «Aucun effet secondaire» ne peuvent pas être appliquées universellement. Vous devez toujours tenir compte du contexte. Un programme sans effets secondaires est littéralement inutile. Même les langages fonctionnels purs reconnaissent cela. Ils s'efforcent plutôt de séparer les parties pures du code de celles ayant des effets secondaires. L’intérêt des monades State ou IO dans Haskell n’est pas d’éviter les effets secondaires - parce que vous ne le pouvez pas - mais que la présence d’effets secondaires est explicitement indiquée par la signature de la fonction.

La règle tell-dont-ask s'applique à un type d'architecture différent - le style dans lequel les objets du programme sont des "acteurs" indépendants communiquant les uns avec les autres. Chaque acteur est fondamentalement autonome et encapsulé. Vous pouvez lui envoyer un message et il décide comment y réagir, mais vous ne pouvez pas examiner l'état interne de l'acteur de l'extérieur. Cela signifie que vous ne pouvez pas savoir si un message modifie l'état interne de l'acteur / de l'objet. L'état et les effets secondaires sont masqués par la conception .

JacquesB
la source
20
@CandiedOrange: le fait qu'une méthode ait des effets secondaires directement ou indirectement, bien que de nombreuses couches d'appels ne changent rien conceptuellement. C'est toujours un effet secondaire. Mais si le fait est que les effets secondaires ne se produisent que par le biais d'objets injectés et que vous contrôlez les types d'effets secondaires possibles, cela ressemble à une bonne conception. Ce n'est tout simplement pas sans effets secondaires. C'est bon OO mais ce n'est pas purement fonctionnel.
JacquesB
12
@LightnessRacesinOrbit: grep en mode silencieux a un code de retour de processus qui sera utilisé. S'il n'en avait pas, il serait vraiment inutile
unperson325680
10
@progo: Un code de retour n'est pas un effet secondaire; c'est l' effet. Tout ce que nous appelons un effet secondaire est appelé un effet secondaire car ce n'est pas le code de retour;)
Lightness Races with Monica
8
@ Cubic c'est le point. Un programme sans effets secondaires est inutile.
Jens Schauder
5
@mathreadler C'est un effet secondaire :)
Andres F.
32

Dites, ne demandez pas vient avec quelques hypothèses fondamentales:

  1. Vous utilisez des objets.
  2. Vos objets ont un état.
  3. L'état de vos objets affecte leur comportement.

Aucune de ces choses ne s'applique aux fonctions pures.

Voyons donc pourquoi nous avons la règle "Dites, ne demandez pas." Cette règle est un avertissement et un rappel. Cela peut se résumer comme ceci:

Permettez à votre classe de gérer son propre état. Ne lui demandez pas son état et agissez ensuite en fonction de cet état. Dites à la classe ce que vous voulez et laissez-la décider quoi faire en fonction de son propre état.

En d'autres termes, les classes sont seules responsables de maintenir leur propre état et d'agir en conséquence. C'est ce que l'encapsulation est tout.

De Fowler :

Tell-Don't-Ask est un principe qui aide les gens à se rappeler que l'orientation objet consiste à associer des données à des fonctions qui fonctionnent sur ces données. Cela nous rappelle qu'au lieu de demander des données à un objet et d'agir sur celles-ci, nous devrions plutôt lui dire quoi faire. Cela nous encourage à déplacer le comportement dans un objet pour aller avec les données.

Pour répéter, rien de tout cela n'a à voir avec des fonctions pures, ni même impures, sauf si vous exposez l'état d'une classe au monde extérieur. Exemples:

Violation de la TDA

var color = trafficLight.Color;
var elapsed = trafficLight.Elapsed;
If (color == Color.Red && elapsed > 2.Minutes)
    trafficLight.ChangeColor(green);

Pas une violation de la TDA

var result = trafficLight.ChangeColor(Color.Green);

ou

var result = await trafficLight.ChangeColorWhenReady(Color.Green);     

Dans les deux derniers exemples, le feu de signalisation conserve le contrôle de son état et de ses actions.

Robert Harvey
la source
Attendez une seconde, les fermetures peuvent être pures. ils ont un état (ils l'appellent la portée lexicale). et cette portée lexicale peut affecter leur comportement. Êtes-vous sûr que TDA n'est pertinent que lorsque des objets sont impliqués?
candied_orange
7
Les fermetures @CandiedOrange ne sont pures que si vous ne modifiez pas les liaisons fermées. Sinon, vous perdez la transparence référentielle lorsque vous appelez la fonction renvoyée par la fermeture.
Jared Smith
1
@ JaredSmith et n'est-ce pas la même chose quand vous parlez d'objets? N'est-ce pas simplement le problème de l'immutabilité?
candied_orange
1
@bdsl - Et maintenant, vous avez par inadvertance indiqué exactement où chaque discussion de ce type allait avec votre exemple trafficLight.refreshDisplay. Si vous suivez les règles, vous aboutissez à un système extrêmement flexible que seul le codeur d'origine comprend. Je parierais même qu'après quelques années d'interruption, même le codeur d'origine ne comprendra probablement pas ce qu'il a fait non plus.
Dunk
1
"Stupide", comme dans "N'ouvrez pas les autres objets et ne regardez pas leurs tripes, mais plutôt répandez vos propres tripes (si vous en avez) dans les méthodes des autres objets, c'est The Way" -silly.
Joker_vD
30

Quand je traite un objet, je ne lui pose pas de question sur son état interne. Je lui dis ce que je dois faire et il utilise son état interne pour comprendre quoi faire avec ce que je lui ai dit de faire.

Vous ne demandez pas seulement son état interne , vous ne demandez pas non plus s'il y a un état interne .

Dites aussi , ne demandez pas! n'implique pas de ne pas obtenir un résultat sous la forme d'une valeur de retour (fournie par une returninstruction dans la méthode). Cela implique simplement que je ne me soucie pas de la façon dont vous le faites, mais faites ce traitement! . Et parfois, vous voulez tout de suite le résultat des traitements ...

Timothy Truckle
la source
1
CQS impliquerait toutefois que la modification de l'état et l'obtention des résultats doivent être séparés
jk.
7
@jk. Comme d'habitude: en général, vous devez séparer les changements d'état et renvoyer un résultat, mais dans de rares cas, il existe des raisons valables de les combiner. Exemple: une next()méthode d' itérateurs ne doit pas seulement renvoyer l'objet actuel, mais également changer son état interne afin que l'appel suivant renvoie l'objet suivant ...
Timothy Truckle
4
Exactement. Je pense que le problème d'OP est simplement dû à une incompréhension / une mauvaise application: «Dis, ne demande pas». Et résoudre ce malentendu fait disparaître le problème.
Konrad Rudolph
@ KonradRudolph Plus, je ne pense pas que ce soit le seul malentendu ici. Leur description d'une "fonction pure" inclut "Son état n'est défini qu'une fois". Un autre commentaire indique que cela pourrait signifier le contexte d'une fermeture, mais la formulation me semble étrange.
Izkata
17

Si vous considérez returncomme "nuisible" (pour rester dans votre image), alors au lieu de faire une fonction comme

ResultType f(InputType inputValue)
{
     // ...
     return result;
}

construisez-le de manière à laisser passer les messages:

void f(InputType inputValue, Action<ResultType> g)
{
     // ...
     g(result);
}

Tant que fet gsont libres d'effets secondaires, les enchaînant sera effet secondaire libre aussi bien. Je pense que ce style est similaire à ce qu'on appelle aussi le style Continuation-passing .

Si cela conduit vraiment à de "meilleurs" programmes, on peut en débattre, car cela rompt certaines conventions. L'ingénieur logiciel allemand Ralf Westphal a créé tout un modèle de programmation autour de cela, il l'a appelé "Event Based Components" avec une technique de modélisation qu'il appelle "Flow Design".

Pour voir des exemples, commencez par la section "Traduction en événements" de cette entrée de blog . Pour l’approche complète, je recommande son livre électronique "La messagerie en tant que modèle de programmation: faire de la POO comme si vous le vouliez" .

Doc Brown
la source
24
If this really leads to "better" programs is debatableIl suffit de regarder le code écrit en JavaScript au cours de la première décennie de ce siècle. Jquery et ses plugins étaient sujets aux callbacks de paradigmes ... callbacks partout . À un moment donné, trop de rappels imbriqués ont fait du débogage un cauchemar. Le code doit encore être lu par les humains quelles que soient les excentricités du génie logiciel et ses "principes"
Laiv
1
même à un moment donné, vous devez fournir une action qui produit un effet secondaire ou vous avez besoin d’un moyen de sortir de la partie CPS
jk.
12
@Laiv CPS a été inventé comme une technologie d'optimisation pour les compilateurs, personne ne s'attendait réellement à ce que les programmeurs écrivent le code de cette façon à la main.
Joker_vD
3
@CandiedOrange Sous forme de slogan, "return, c'est simplement dire à la suite ce qu'il faut faire". En effet, la création de Scheme a été motivée par une tentative de compréhension du modèle d'acteur de Hewitt et est parvenue à la conclusion que les acteurs et les clôtures étaient la même chose.
Derek Elkins
2
En théorie, bien sûr, vous pourriez écrire l’ensemble de votre application sous la forme d’une série d’appels de fonction qui ne renvoient rien. Si cela ne vous dérange pas d'être couplé étroitement, vous n'avez même pas besoin des ports. Mais la plupart des applications saines utilisent des fonctions qui renvoient des choses, parce que… eh bien, elles sont saines. Et comme je crois l'avoir suffisamment démontré dans ma réponse, vous pouvez returnutiliser des données à partir de fonctions tout en adhérant à Tell Don't Ask, tant que vous ne contrôlez pas l'état d'un objet.
Robert Harvey
7

Le passage de message est intrinsèquement efficace. Si vous demandez à un objet de faire quelque chose, vous vous attendez à ce qu'il ait un effet sur quelque chose. Si le gestionnaire de messages était pur, vous n'avez pas besoin de lui envoyer un message.

Dans les systèmes d'acteurs distribués, le résultat d'une opération est généralement envoyé sous forme de message à l' expéditeur de la demande d'origine. L'expéditeur du message est soit implicitement mis à disposition par le runtime de l'acteur, soit il est (par convention) explicitement passé en tant que partie du message. En mode de transmission synchrone, une réponse unique s'apparente à une returninstruction. Lors de la transmission asynchrone de messages, l'utilisation de messages de réponse est particulièrement utile car elle permet un traitement simultané dans plusieurs acteurs tout en fournissant des résultats.

Le passage de "l'expéditeur" auquel le résultat doit être envoyé explicitement modélise fondamentalement le style de passage de continuation ou les paramètres redoutés - à l'exception du fait qu'il leur transmet des messages au lieu de les muter directement.

Bergi
la source
5

Toute cette question me semble être une «violation de niveau».

Vous avez (au moins) les niveaux suivants dans un projet majeur:

  • Le niveau système, par exemple la plate-forme de commerce électronique
  • Le niveau de sous-système, par exemple validation utilisateur: serveur, AD, front-end
  • Le niveau de programme individuel, par exemple une des composantes de ce qui précède
  • Le niveau d'acteur / module [cela devient sombre selon la langue]
  • Le niveau de méthode / fonction.

Et ainsi de suite jusqu'aux jetons individuels.

Il n'est pas vraiment nécessaire qu'une entité au niveau de la méthode / fonction ne retourne pas (même si elle retourne simplement this). Et il n’est pas nécessaire (dans votre description) de demander à une entité au niveau acteur de restituer quoi que ce soit (en fonction du langage, cela peut même ne pas être possible). Je pense que la confusion provient de la fusion de ces deux niveaux, et je dirais qu'ils devraient faire l’objet d’un raisonnement distinct (même si un objet donné couvre en fait plusieurs niveaux).

Jared Smith
la source
2

Vous indiquez que vous souhaitez vous conformer à la fois au principe OOP de "Dire, ne demandez pas" et au principe fonctionnel des fonctions pures, mais je ne vois pas très bien comment cela vous a conduit à fuir la déclaration.

Une façon relativement courante de suivre ces deux principes consiste à utiliser les déclarations de retour et à utiliser des objets immuables uniquement avec des accesseurs. L’approche consiste alors à demander à certains des getters de renvoyer un objet similaire avec un nouvel état, par opposition à une modification de l’état de l’objet original.

Un exemple de cette approche est fourni avec Python tupleet frozensetles types de données. Voici une utilisation typique d'un frozenset:

small_digits = frozenset([0, 1, 2, 3, 4])
big_digits = frozenset([5, 6, 7, 8, 9])
all_digits = small_digits.union(big_digits)

print("small:", small_digits)
print("big:", big_digits)
print("all:", all_digits)

Ce qui imprimera ce qui suit, démontrant que la méthode d'union crée un nouveau frozenset avec son propre état sans affecter les anciens objets:

petit: frozenset ({0, 1, 2, 3, 4})

big: frozenset ({5, 6, 7, 8, 9})

tout: frozenset ({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})

Un autre exemple complet de structures de données immuables similaires est la bibliothèque Immutable.js de Facebook . Dans les deux cas, vous commencez avec ces blocs de construction et pouvez créer des objets de domaine de niveau supérieur qui suivent les mêmes principes, en obtenant une approche OOP fonctionnelle, qui vous aide à encapsuler les données et à les raisonner plus facilement. Et l'immuabilité vous permet également de tirer parti de la possibilité de partager de tels objets entre des threads sans vous soucier des verrous.

YoniLavi
la source
1

Je suis venu pour suspecter qu'un code bien écrit peut suivre les principes d'OO et les principes fonctionnels en même temps. J'essaie de réconcilier ces idées et le gros problème que j'ai rencontré est le retour.

J'ai essayé de faire de mon mieux pour concilier certains des avantages de la programmation impérative et fonctionnelle (bien sûr, je ne reçois pas tous les avantages, mais j'essaie d'obtenir la part du lion des deux), bien que ce returnsoit fondamental pour le faire en une méthode simple pour moi dans de nombreux cas.

Pour ce qui est d’essayer d’éviter returncarrément les déclarations, j’ai essayé de réfléchir à cela au cours des dernières heures et, en gros, la pile a débordé à plusieurs reprises dans mon cerveau. Je peux voir son intérêt en termes de renforcement du plus haut niveau d’encapsulation et d’informations se cachant en faveur d’objets très autonomes à qui il est simplement demandé ce qu’il faut faire, et j’aime explorer les extrémités des idées, ne serait-ce que pour essayer d’obtenir une meilleure compréhension de la façon dont ils travaillent.

Si nous prenons l'exemple des feux de signalisation, une tentative naïve voudrait immédiatement lui donner une connaissance du monde entier qui l'entoure, ce qui serait certainement indésirable du point de vue du couplage. Donc, si je comprends bien, vous résumez cela et dissociez plutôt en faveur de la généralisation du concept de ports d'E / S qui propagent davantage les messages et les requêtes, et non les données, via le pipeline, et injectent essentiellement ces objets avec les interactions / requêtes souhaitées tout en oubliant les uns des autres.

Le pipeline nodal

entrez la description de l'image ici

Et ce diagramme est à peu près aussi long que j'ai essayé de le dessiner (et bien que simple, j'ai dû le changer et le repenser). Immédiatement, j'ai tendance à penser qu'un dessin avec ce niveau de découplage et d'abstraction trouverait son chemin devenant très difficile à raisonner sous forme de code, car le ou les orchestrateurs qui câblent toutes ces choses pour un monde complexe pourraient avoir beaucoup de difficulté à garder une trace de toutes ces interactions et demandes afin de créer le pipeline souhaité. Sous forme visuelle, toutefois, il peut être assez simple de simplement dessiner ces éléments sous forme de graphique, de tout relier et de voir les choses se dérouler de manière interactive.

En termes d’effets secondaires, j’ai pu constater que cela n’était pas dû à des "effets secondaires" dans le sens où ces demandes pouvaient, sur la pile d’appel, conduire à une chaîne de commandes pour chaque thread à exécuter, par exemple (je ne compte pas cela en tant qu '"effet secondaire" au sens pragmatique du fait que cela ne modifie aucun état pertinent pour le monde extérieur tant que de telles commandes ne sont pas exécutées - l'objectif pratique pour la plupart des logiciels est de ne pas éliminer les effets secondaires, mais de les différer et de les centraliser) . De plus, l'exécution de la commande peut générer un nouveau monde, par opposition à la mutation du monde existant. Mon cerveau est vraiment fatigué juste d'essayer de comprendre tout cela, cependant, en l'absence de toute tentative de prototyper ces idées. J'ai aussi pas

Comment ça marche

Donc, pour clarifier, j’imaginais comment vous programmez cela. La façon dont je le voyais fonctionner était en fait le diagramme ci-dessus qui capturait le flux de travail de l'utilisateur final (programmeur). Vous pouvez faire glisser un feu de signalisation dans le monde, faire glisser une minuterie, lui donner une période écoulée (après l'avoir "construite"). La minuterie a un On Intervalévénement (port de sortie), vous pouvez le connecter au feu afin que, sur de tels événements, il indique au feu de faire défiler ses couleurs.

Le feu tricolore peut alors, lors du passage à certaines couleurs, émettre des sorties (événements) telles que, On Redà quel point nous pouvons traîner un piéton dans notre monde et faire en sorte que cet événement dise au piéton de commencer à marcher ... ou que nous puissions traîner des oiseaux notre scène et faire en sorte que lorsque la lumière devient rouge, nous disons aux oiseaux de commencer à voler et à battre des ailes ... ou peut-être lorsque la lumière devient rouge, nous disons à une bombe d'exploser - tout ce que nous voulons, et avec les objets complètement inconscients les uns des autres, et ne rien faire à part se dire indirectement quoi faire à travers ce concept abstrait d’entrée / sortie.

Et ils encapsulent pleinement leur état et ne révèlent rien à leur sujet (à moins que ces "événements" soient considérés comme des TMI, à ce moment-là, je devrai beaucoup repenser), ils se disent des choses à faire indirectement, ils ne le demandent pas. Et ils sont très découplés. Rien ne sait rien d'autre que cette abstraction généralisée des ports d'entrée / sortie.

Cas d'utilisation pratique?

Je pouvais voir ce genre de chose être utile comme langage intégré de haut niveau spécifique à un domaine dans certains domaines pour orchestrer tous ces objets autonomes qui ignorent tout du monde environnant, n'exposent rien de leur état interne post-construction et ne font que propager des requêtes entre nous, que nous pouvons changer et adapter au contenu de notre cœur. Pour le moment, j’ai l’impression que cela est très spécifique à un domaine, ou peut-être n’ai-je pas suffisamment réfléchi à la question, car il m’est très difficile de cerner le type de choses que je développe régulièrement (je travaille souvent avec code plutôt bas-moyen) si je devais interpréter Tell, Don't Ask à de telles extrémités et que je voulais le plus haut niveau d'encapsulation imaginable. Mais si nous travaillons avec des abstractions de haut niveau dans un domaine spécifique,

Signaux et Slots

Cette conception m’était étrangement familière jusqu’à ce que j’ai réalisé qu’il s’agissait essentiellement de signaux et de créneaux horaires si nous ne prenons pas beaucoup en compte les nuances de son implémentation. La principale question qui se pose à moi est de savoir avec quelle efficacité nous pouvons programmer ces nœuds individuels (objets) dans le graphe comme étant strictement conformes à Tell, Don't Ask, pris dans la mesure du possible return, et si nous pouvons évaluer ledit graphe sans mutations parallèle, par exemple absence de verrouillage). C'est là que les avantages magiques ne résident pas dans la manière dont nous connectons potentiellement ces éléments, mais dans la manière dont ils peuvent être mis en œuvre à ce degré d'encapsulation sans mutations. Ces deux solutions me semblent réalisables, mais je ne sais pas dans quelle mesure elles seraient applicables, et c’est pourquoi je suis un peu perplexe d’essayer de résoudre des cas d’utilisation potentiels.

Énergie de dragon
la source
0

Je vois clairement une fuite de certitude ici. Il semble que «effet secondaire» soit un terme bien connu et communément compris, mais en réalité il ne l’est pas. En fonction de vos définitions (qui manquent en réalité dans le PO), des effets secondaires peuvent être totalement nécessaires (comme l'explique @JacquesB), ou sans aucune acceptation. Ou bien, faisant un pas de plus vers la clarification, il est nécessaire de faire la distinction entre les effets secondaires désirés que l’ on n’aime pas dissimuler (à ce stade, le célèbre IO de Haskell apparaît: ce n’est qu’un moyen d’être explicite) et les effets secondaires non désirés. résultat de bugs de code et ce genre de choses . Ce sont des problèmes assez différents et nécessitent donc un raisonnement différent.

Je suggère donc de commencer par vous reformuler: "Comment définissons-nous les effets secondaires et que disent les définitions à propos de son interrelation avec la déclaration" return "?".

Sereja Bogolubov
la source
1
"à ce stade, le célèbre OI de Haskell émerge: ce n'est qu'un moyen d'être explicite" - le caractère explicite est certes un avantage du mono OI de Haskell, mais il a un autre point: il fournit un moyen d'isoler l'effet secondaire entièrement en dehors du langage - bien que les implémentations courantes ne le fassent pas en réalité , principalement pour des raisons d'efficacité, il est vrai sur le plan conceptuel: la monade IO peut être considérée comme un moyen de renvoyer une instruction à un environnement entièrement extérieur à l'environnement. Programme Haskell, avec une référence à une fonction à poursuivre après l’achèvement.
Jules