Il ritorno è considerato dannoso? Il codice può funzionare senza di esso?


45

OK, quindi il titolo è un po 'clickbaity ma seriamente sono stato su un tell, non chiedere calcio per un po'. Mi piace come incoraggia i metodi da utilizzare come messaggi in modo orientato agli oggetti. Ma questo ha un fastidioso problema che mi ha sconvolto nella testa.

Sono arrivato a sospettare che un codice ben scritto possa seguire contemporaneamente i principi OO e i principi funzionali. Sto cercando di conciliare queste idee e il grande punto critico su cui sono arrivato è return.

Una funzione pura ha due qualità:

  1. Chiamarlo ripetutamente con gli stessi input dà sempre lo stesso risultato. Ciò implica che è immutabile. Il suo stato è impostato solo una volta.

  2. Non produce effetti collaterali. L'unico cambiamento causato dal chiamarlo sta producendo il risultato.

Quindi, come si fa a essere puramente funzionali se si è giurato di usare returncome modo di comunicare i risultati?

Il tell, non chiedere idea funziona utilizzando ciò che alcuni considererebbero un effetto collaterale. Quando ho a che fare con un oggetto non lo chiedo del suo stato interno. Gli dico quello che devo fare e usa il suo stato interno per capire cosa fare con quello che gli ho detto di fare. Una volta che lo dico, non chiedo cosa abbia fatto. Mi aspetto solo che abbia fatto qualcosa per quello che gli è stato detto di fare.

Penso a Tell, Don't Ask come qualcosa di più di un nome diverso per l'incapsulamento. Quando uso returnnon ho idea di come mi abbia chiamato. Non posso parlare del suo protocollo, devo forzarlo a gestire il mio protocollo. Che in molti casi viene espresso come stato interno. Anche se ciò che è esposto non è esattamente stato, di solito è solo un calcolo eseguito su argomenti di stato e input. Avere un'interfaccia per rispondere attraverso offre la possibilità di massaggiare i risultati in qualcosa di più significativo dello stato interno o dei calcoli. Questo è il passaggio di messaggi . Vedere questo esempio .

Nel lontano passato, quando in realtà le unità disco contenevano dischi e una chiavetta USB era ciò che facevi in ​​macchina quando la ruota era troppo fredda per toccarla con le dita, mi hanno insegnato come le persone fastidiose considerino le funzioni che hanno parametri fuori. void swap(int *first, int *second)sembrava così utile, ma siamo stati incoraggiati a scrivere funzioni che hanno restituito i risultati. Quindi ho preso a cuore questo sulla fede e ho iniziato a seguirlo.

Ma ora vedo persone che costruiscono architetture in cui gli oggetti lasciano che il modo in cui sono stati costruiti controlla dove inviano i loro risultati. Ecco un esempio di implementazione . L'iniezione dell'oggetto porta di output sembra un po 'come l'idea del parametro out da capo. Ma è così che gli oggetti Tell-Don't-Ask raccontano agli altri cosa hanno fatto.

Quando ho appreso per la prima volta degli effetti collaterali, l'ho pensato come il parametro di output. Ci è stato detto di non sorprendere le persone facendo in modo che alcuni dei lavori si svolgessero in modo sorprendente, cioè non seguendo la return resultconvenzione. Ora certo, so che c'è un mucchio di problemi di threading asincrono parallelo con cui si nascondono gli effetti collaterali, ma il ritorno è davvero solo una convenzione che ti fa lasciare il risultato spinto nello stack in modo che qualunque cosa tu chiami, possa saltar fuori in seguito. Questo è tutto.

Cosa sto davvero cercando di chiedere:

È returnl'unico modo per evitare tutta quella sofferenza dell'effetto collaterale e ottenere la sicurezza del thread senza blocchi, ecc. O posso seguire dire, non chiedere in modo puramente funzionale?


3
Se scegli di non ignorare la separazione delle query dei comandi, considereresti risolto il problema?
rwong

30
Considera che trovarti su un calcio potrebbe essere un'indicazione che stai impegnando in un design basato sui dogmi piuttosto che ragionare sui pro e contro di ogni situazione specifica.
Blrfl,

3
In generale, quando le persone parlano di "ritorno" dannoso, dicono che è contro la programmazione strutturata, non funzionale, e una singola dichiarazione di ritorno alla fine della routine (e forse alla fine di entrambe le parti di un blocco if / else che è esso stesso l'ultimo elemento) non è incluso in questo.
Casuale 832,

16
@jameslarge: falsa dicotomia. Non permettere a te stesso di essere guidato ai progetti dal pensiero dogmatico non è la stessa cosa del selvaggio West / tutto va di pari passo con l'approccio di cui stai parlando. Il punto è non permettere al dogma di intralciare un codice buono, semplice, ovvio. La legge universale è per i carenti; il contesto è per i re.
Nicol Bolas,

4
C'è una cosa per me che non è chiara nella tua domanda: dici di aver giurato di usare "return", ma per quanto posso dire non collegarlo esplicitamente agli altri tuoi concetti o dire perché l'hai fatto. Se combinato con la definizione di una funzione pura che include la produzione di un risultato, si crea qualcosa che è impossibile da risolvere.
Danikov,

Risposte:


96

Se una funzione non ha effetti collaterali e non restituisce nulla, la funzione è inutile. È così semplice.

Ma immagino che tu possa usare alcuni trucchi se vuoi seguire la lettera delle regole e ignorare il ragionamento sottostante. Ad esempio, usare un parametro out significa in senso stretto non usare un ritorno. Ma fa esattamente lo stesso di un ritorno, solo in un modo più contorto. Quindi, se ritieni che il ritorno sia negativo per un motivo , l'utilizzo di un parametro out è chiaramente negativo per gli stessi motivi sottostanti.

Puoi usare trucchi più contorti. Ad esempio Haskell è famoso per il trucco della monade IO in cui si possono avere effetti collaterali nella pratica, ma ancora non strettamente parlando hanno effetti collaterali da un punto di vista teorico. Lo stile di passaggio di continuazione è un altro trucco, che ti consente di evitare i ritorni al prezzo di trasformare il tuo codice in spaghetti.

La linea di fondo è che, in assenza di trucchi sciocchi, i due principi di funzioni gratuite senza effetti collaterali e "nessun ritorno" non sono semplicemente compatibili. Inoltre, sottolineo che entrambi sono principi veramente cattivi (dogmi davvero) in primo luogo, ma questa è una discussione diversa.

Regole come "dire, non chiedere" o "nessun effetto collaterale" non possono essere applicate universalmente. Devi sempre considerare il contesto. Un programma senza effetti collaterali è letteralmente inutile. Anche i linguaggi funzionali puri lo riconoscono. Piuttosto si sforzano di separare le parti pure del codice da quelle con effetti collaterali. Il punto delle monadi di Stato o IO in Haskell non è che si evitano gli effetti collaterali - perché non è possibile - ma che la presenza di effetti collaterali è esplicitamente indicata dalla firma della funzione.

La regola tell-dont-ask si applica a un diverso tipo di architettura - lo stile in cui gli oggetti nel programma sono "attori" indipendenti che comunicano tra loro. Ogni attore è sostanzialmente autonomo e incapsulato. Puoi inviargli un messaggio e decide come reagire ad esso, ma non puoi esaminare lo stato interno dell'attore dall'esterno. Ciò significa che non è possibile sapere se un messaggio modifica lo stato interno dell'attore / oggetto. Lo stato e gli effetti collaterali sono nascosti dal design .


20
@CandiedOrange: se un metodo ha effetti collaterali direttamente o indirettamente sebbene molti livelli di chiamate non cambino nulla concettualmente. È ancora un effetto collaterale. Ma se il punto è che gli effetti collaterali si verificano solo attraverso oggetti iniettati in modo da controllare quali tipi di effetti collaterali sono possibili, allora sembra un buon design. Non è privo di effetti collaterali. È buono OO ma non è puramente funzionale.
Jacques B

12
@LightnessRacesinOrbit: grep in modalità non interattiva ha un codice di ritorno del processo che verrà utilizzato. Se non lo avesse fatto, sarebbe davvero inutile
unperson325680,

10
@progo: un codice di ritorno non è un effetto collaterale; è l' effetto. Qualunque cosa che chiamiamo un effetto collaterale è chiamato un effetto collaterale perché non è il codice di ritorno;)
Lightness Races con Monica

8
@Cubic questo è il punto. Un programma senza effetti collaterali è inutile.
Jens Schauder,

5
@mathreadler Questo è un effetto collaterale :)
Andres F.

32

Tell, Don't Ask presenta alcuni presupposti fondamentali:

  1. Stai usando oggetti.
  2. I tuoi oggetti hanno stato.
  3. Lo stato dei tuoi oggetti influenza il loro comportamento.

Nessuna di queste cose si applica alle funzioni pure.

Quindi rivediamo perché abbiamo la regola "Tell, Don't Ask". Questa regola è un avvertimento e un promemoria. Può essere riassunto in questo modo:

Consenti alla tua classe di gestire il proprio stato. Non chiederlo per il suo stato, quindi agire in base a quello stato. Di 'alla classe quello che vuoi e lascia che decida cosa fare in base al suo stato.

Per dirla in altro modo, le classi sono le uniche responsabili del mantenimento del proprio stato e dell'azione su di esso. Questo è l'incapsulamento.

Da Fowler :

Tell-Don't-Ask è un principio che aiuta le persone a ricordare che l'orientamento agli oggetti riguarda il raggruppamento di dati con le funzioni che operano su tali dati. Ci ricorda che invece di chiedere a un oggetto dati e agire su tali dati, dovremmo invece dire a un oggetto cosa fare. Questo ci incoraggia a spostare il comportamento in un oggetto per andare con i dati.

Per ribadire, nulla di tutto ciò ha a che fare con funzioni pure, o addirittura impure, a meno che tu non stia esponendo lo stato di una classe al mondo esterno. Esempi:

Violazione della TDA

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

Non una violazione della TDA

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

o

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

In entrambi questi ultimi esempi, il semaforo mantiene il controllo del suo stato e delle sue azioni.


Aspetta un secondo, le chiusure possono essere pure. hanno uno stato (lo chiamano ambito lessicale). e che l'ambito lessicale può influenzare il loro comportamento. Sei sicuro che TDA sia rilevante solo quando sono coinvolti oggetti?
candied_orange,

7
Le chiusure di @CandiedOrange sono pure solo se non si modificano le associazioni chiuse. Altrimenti si perde la trasparenza referenziale quando si chiama la funzione restituita dalla chiusura.
Jared Smith,

1
@JaredSmith e non è lo stesso quando parli di oggetti? Non è semplicemente questo il problema dell'immutabilità?
candied_orange,

1
@bdsl - E ora hai inavvertitamente sottolineato esattamente dove ogni discussione di questo tipo va con il tuo esempio trafficLight.refreshDisplay. Se segui le regole, porti a un sistema altamente flessibile che nessuno tranne il programmatore originale capisce. Scommetto persino che dopo un paio d'anni anche il programmatore originale probabilmente non capirà cosa hanno fatto.
Dunk

1
"Stupido", come in "Non aprire gli altri oggetti e non guardare le loro viscere, ma versa le tue viscere (se ne hai) nei metodi degli altri oggetti; questa è la Via" -silly.
Joker_vD,

30

Quando ho a che fare con un oggetto non lo chiedo del suo stato interno. Gli dico quello che devo fare e usa il suo stato interno per capire cosa fare con quello che gli ho detto di fare.

Non chiedi solo il suo stato interno , non chiedi nemmeno se ha uno stato interno .

Inoltre , non chiedere! non non implica non ottenere un risultato in forma di un valore di ritorno (fornito da un returncomunicato all'interno del metodo). Implica semplicemente che non mi interessa come lo fai, ma lo faccio! . E a volte vuoi immediatamente il risultato del trattamento ...


1
CQS implicherebbe tuttavia che la modifica dello stato e il raggiungimento dei risultati dovrebbero essere separati
jk.

7
@jk. Come al solito: in generale dovresti separare il cambio di stato e restituire un risultato, ma in rari casi ci sono validi motivi per combinarlo. Ad esempio: un next()metodo iteratori non dovrebbe solo restituire l'oggetto corrente, ma anche cambiare lo stato interno degli iteratori in modo che la chiamata successiva restituisca l'oggetto successivo ...
Timothy Truckle,

4
Esattamente. Penso che il problema di OP sia semplicemente dovuto a fraintendimenti / errata applicazione del "dire, non chiedere". E risolvendo questo malinteso, il problema scompare.
Konrad Rudolph,

@KonradRudolph Plus, non credo sia l'unico malinteso qui. La loro descrizione di una "funzione pura" include "Il suo stato è impostato una sola volta". Un altro commento indica che potrebbe significare il contesto di una chiusura, ma il fraseggio mi sembra strano.
Izkata,

17

Se consideri return"dannoso" (per rimanere nella tua foto), invece di fare una funzione simile

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

costruirlo in un modo che passi messaggi:

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

Finché fe gsono privi di effetti collaterali, concatenarli insieme sarà anche privo di effetti collaterali. Penso che questo stile sia simile a quello che viene anche chiamato stile di passaggio di continuazione .

Se questo porta davvero a programmi "migliori" è discutibile, poiché infrange alcune convenzioni. L'ingegnere del software tedesco Ralf Westphal ha realizzato un intero modello di programmazione attorno a questo, lo ha chiamato "Event Based Components" con una tecnica di modellazione che chiama "Flow Design".

Per vedere alcuni esempi, inizia nella sezione "Traduzione agli eventi" di questo post di blog . Per l'approccio completo, raccomando il suo e-book "Messaggi come modello di programmazione: fare OOP come se volessi dire" .


24
If this really leads to "better" programs is debatableDobbiamo solo guardare il codice scritto in JavaScript durante il primo decennio di questo secolo. Jquery e i suoi plugin erano inclini a questo paradigma callback ... callback ovunque . A un certo punto, troppi callback nidificati hanno reso il debug un incubo. Il codice deve ancora essere letto dagli umani, indipendentemente dalle eccentricità dell'ingegneria del software e dai suoi "principi"
Laiv,

1
anche allora ad un certo punto devi fornire un'azione che comporti un effetto collaterale o hai bisogno di un modo per uscire dalla parte CPS
jk.

12
@Laiv CPS è stato inventato come una tecnologia di ottimizzazione per i compilatori, nessuno si aspettava che i programmatori scrivessero il codice in questo modo a mano.
Joker_vD,

3
@CandiedOrange In forma di slogan, "return sta semplicemente dicendo alla continuazione cosa fare". In effetti, la creazione di Scheme è stata motivata dal tentativo di comprendere il modello di attore di Hewitt e ha concluso che attori e chiusure erano la stessa cosa.
Derek Elkins,

2
Ipoteticamente, certo, potresti scrivere l'intera applicazione come una serie di chiamate di funzione che non restituiscono nulla. Se non ti dispiace che sia strettamente accoppiato, non hai nemmeno bisogno delle porte. Ma la maggior parte delle applicazioni sane utilizzano funzioni che restituiscono cose, perché ... beh, sono sane. E come credo di aver adeguatamente dimostrato nella mia risposta, è possibile returndati dalle funzioni e comunque aderire a Tell Don't Ask, purché non si controlli lo stato di un oggetto.
Robert Harvey,

7

Il passaggio dei messaggi è intrinsecamente efficace. Se dici a un oggetto di fare qualcosa, ti aspetti che abbia un effetto su qualcosa. Se il gestore messaggi era puro, non sarebbe necessario inviargli un messaggio.

Nei sistemi di attori distribuiti, il risultato di un'operazione viene generalmente inviato come messaggio al mittente della richiesta originale. Il mittente del messaggio viene reso implicitamente disponibile dal runtime dell'attore oppure (per convenzione) viene esplicitamente passato come parte del messaggio. Nel passaggio sincrono dei messaggi, una singola risposta è simile a returnun'istruzione. Nel passaggio asincrono dei messaggi, l'utilizzo dei messaggi di risposta è particolarmente utile in quanto consente l'elaborazione simultanea in più attori pur continuando a fornire risultati.

Il passaggio del "mittente" al quale il risultato dovrebbe essere esplicitamente modellato fondamentalmente modella lo stile di passaggio di continuazione oi parametri temuti - tranne che passa loro messaggi invece di mutarli direttamente.


5

Tutta questa domanda mi sembra una "violazione del livello".

Hai (almeno) i seguenti livelli in un grande progetto:

  • Il livello di sistema, ad es. Piattaforma di e-commerce
  • Il livello del sottosistema, ad esempio la convalida dell'utente: server, AD, front-end
  • Il livello del singolo programma, ad es. Uno dei componenti di cui sopra
  • Il livello attore / modulo [questo diventa oscuro a seconda della lingua]
  • Il livello metodo / funzione.

E così via fino ai singoli token.

Non c'è davvero alcuna necessità che un'entità a livello di metodo / funzione non ritorni (anche se semplicemente ritorna this). E non c'è (nella descrizione) alcuna necessità per un'entità a livello di Attore di restituire qualcosa (a seconda della lingua che potrebbe non essere nemmeno possibile). Penso che la confusione sia nel confondere questi due livelli, e direi che dovrebbero essere ragionati in modo distinto (anche se un dato oggetto si estende su più livelli).


2

Dici che vuoi conformarti sia al principio OOP di "dire, non chiedere" sia al principio funzionale delle funzioni pure, ma non vedo bene come ciò ti abbia portato a evitare la dichiarazione di ritorno.

Un modo alternativo relativamente comune di seguire entrambi questi principi è quello di andare all-in sulle dichiarazioni di ritorno e usare oggetti immutabili solo con getter. L'approccio quindi è quello di avere alcuni getter che restituiscono un oggetto simile con un nuovo stato, invece di cambiare lo stato dell'oggetto originale.

Un esempio di questo approccio è nell'integrato di Python tuplee nei frozensettipi di dati. Ecco un uso tipico di 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)

Che stamperà quanto segue, dimostrando che il metodo union crea un nuovo frozenset con il suo stato senza influenzare i vecchi oggetti:

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

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

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

Un altro ampio esempio di strutture di dati immutabili simili è la libreria Immutable.js di Facebook . In entrambi i casi, inizi con questi blocchi predefiniti e puoi costruire oggetti di dominio di livello superiore che seguono gli stessi principi, ottenendo un approccio OOP funzionale, che ti aiuti a incapsulare i dati e ragionare più facilmente. E l'immutabilità ti consente anche di trarre vantaggio dalla possibilità di condividere tali oggetti tra i thread senza doversi preoccupare dei blocchi.


1

Sono arrivato a sospettare che un codice ben scritto possa seguire contemporaneamente i principi OO e i principi funzionali. Sto cercando di conciliare queste idee e il grande punto critico su cui sono atterrato è il ritorno.

Ho fatto del mio meglio per conciliare alcuni dei vantaggi, in particolare, della programmazione imperativa e funzionale (naturalmente non ottenere tutti i benefici di sorta, ma cercare di ottenere la parte del leone di entrambi), anche se in returnrealtà è fondamentale per farlo in una moda semplice per me in molti casi.

Per quanto riguarda il tentativo di evitare returnapertamente le affermazioni, ho cercato di rimuginare su questo per circa un'ora e in pratica lo stack ha traboccato il mio cervello un numero di volte. Riesco a vederne il fascino in termini di applicazione del più forte livello di incapsulamento e di occultamento delle informazioni a favore di oggetti molto autonomi a cui viene semplicemente detto cosa fare, e mi piace esplorare le estremità delle idee, se non altro per cercare di migliorare comprensione di come funzionano.

Se usiamo l'esempio del semaforo, immediatamente un tentativo ingenuo vorrebbe dare una tale conoscenza del semaforo del mondo intero che lo circonda e che sarebbe certamente indesiderabile dal punto di vista dell'accoppiamento. Quindi, se capisco correttamente, lo toglie e disaccoppiate a favore della generalizzazione del concetto di porte I / O che propagano ulteriormente messaggi e richieste, non dati, attraverso la pipeline e fondamentalmente iniettano questi oggetti con le interazioni / richieste desiderate tra di loro mentre dimentico l'uno dell'altro.

La pipeline nodale

inserisci qui la descrizione dell'immagine

E quel diagramma riguarda quasi quanto ho provato a disegnarlo (e mentre semplice, ho dovuto continuare a cambiarlo e ripensarlo). Immediatamente tendo a pensare che un progetto con questo livello di disaccoppiamento e astrazione possa diventare molto difficile ragionare in forma di codice, perché gli orchestratori che collegano tutte queste cose per un mondo complesso potrebbero trovare molto difficile tenere traccia di tutte queste interazioni e richieste al fine di creare la pipeline desiderata. In forma visiva, tuttavia, potrebbe essere ragionevolmente semplice disegnare queste cose come un grafico e collegare tutto e vedere le cose accadere in modo interattivo.

In termini di effetti collaterali, ho potuto constatare che questo era privo di "effetti collaterali", nel senso che queste richieste potevano, nello stack di chiamate, portare a una catena di comandi per l'esecuzione di ogni thread, ad esempio (non lo conto come "effetto collaterale" in senso pragmatico in quanto non altera alcuno stato rilevante per il mondo esterno fino a quando tali comandi non vengono effettivamente eseguiti - l'obiettivo pratico per me nella maggior parte dei software non è eliminare gli effetti collaterali ma rimandarli e centralizzarli) . Inoltre, l'esecuzione del comando potrebbe generare un nuovo mondo invece di mutare quello esistente. Il mio cervello è davvero tassato solo cercando di comprendere tutto ciò, tuttavia, in assenza di qualsiasi tentativo di prototipazione di queste idee. Inoltre non ho

Come funziona

Quindi, per chiarire, stavo immaginando come lo programmate davvero. Il modo in cui lo vedevo funzionare era in realtà il diagramma sopra che catturava il flusso di lavoro dell'utente (programmatore). Puoi trascinare un semaforo nel mondo, trascinare un timer, assegnargli un periodo trascorso (dopo averlo "costruito"). Il timer ha un On Intervalevento (porta di uscita), puoi collegarlo al semaforo in modo che su tali eventi, indichi alla luce di scorrere i suoi colori.

Il semaforo potrebbe quindi, passando a determinati colori, emettere output (eventi) come, On Reda quel punto potremmo trascinare un pedone nel nostro mondo e far sì che l'evento dica al pedone di iniziare a camminare ... o potremmo trascinare gli uccelli in la nostra scena e rendiamola così quando la luce diventa rossa, diciamo agli uccelli di iniziare a volare e sbattere le ali ... o forse quando la luce diventa rossa, diciamo a una bomba di esplodere - qualunque cosa vogliamo, e con gli oggetti che sono completamente ignari l'uno dell'altro, e non facendo altro che dirsi indirettamente cosa fare attraverso questo concetto astratto di input / output.

E incapsulano completamente il loro stato e non rivelano nulla al riguardo (a meno che questi "eventi" non siano considerati TMI, a quel punto dovrei ripensare molto le cose), si dicono reciprocamente cose da fare indirettamente, non chiedono. E sono super disaccoppiati. Nulla sa di nulla tranne questa astrazione generalizzata della porta di input / output.

Casi d'uso pratico?

Ho potuto vedere questo tipo di cose essere utili come un linguaggio incorporato specifico di dominio di alto livello in alcuni domini per orchestrare tutti questi oggetti autonomi che non conoscono nulla del mondo circostante, esporre nulla della loro costruzione interna dello stato e fondamentalmente solo propagare le richieste tra loro che possiamo cambiare e modificare in base al contenuto dei nostri cuori. Al momento mi sembra che questo sia molto specifico del dominio, o forse non ci ho pensato abbastanza, perché è molto difficile per me avvolgere il cervello con i tipi di cose che sviluppo regolarmente (spesso lavoro con piuttosto un livello medio-basso) se dovessi interpretare Tell, Don't Ask a tali estremità e desiderare il livello più forte di incapsulamento immaginabile. Ma se stiamo lavorando con astrazioni di alto livello in un dominio specifico,

Segnali e slot

Questo design mi è sembrato stranamente familiare fino a quando ho capito che è fondamentalmente segnali e slot se non prendiamo molto in considerazione le sfumature del modo in cui è implementato. La domanda principale per me è quanto efficacemente possiamo programmare questi singoli nodi (oggetti) nel grafico come strettamente aderenti a Tell, Don't Ask, presi al livello di evitare returndichiarazioni e se possiamo valutare detto grafico senza mutazioni (in parallelo, ad es. bloccaggio assente). Ecco dove i benefici magici non sono nel modo in cui colleghiamo potenzialmente queste cose insieme, ma come possono essere implementate a questo grado di incapsulamento in assenza di mutazioni. Entrambi questi sembrano fattibili per me, ma non sono sicuro di quanto ampiamente applicabile sarebbe, ed è lì che sono un po 'sconcertato nel tentativo di lavorare su potenziali casi d'uso.


0

Vedo chiaramente perdita di certezza qui. Sembra che "effetto collaterale" sia un termine ben noto e comunemente compreso, ma in realtà non lo è. A seconda delle definizioni (che in realtà mancano nel PO), gli effetti collaterali potrebbero essere totalmente necessari (come è riuscito a spiegare @JacquesB), o senza pietà senza essere accettati. Oppure, facendo un passo verso il chiarimento, è necessario distinguere tra gli effetti collaterali desiderati che non si desidera nascondere (a questo punto emerge il famoso IO di Haskell: non è altro che un modo per essere espliciti) e gli effetti collaterali indesiderati come risultato di bug del codice e cose del genere . Questi sono problemi piuttosto diversi e quindi richiedono ragionamenti diversi.

Quindi, suggerisco di iniziare a riformularti: "Come definiamo l'effetto collaterale e cosa dicono le definizioni fornite sulla sua interrelazione con l'istruzione" return "?".


1
"a questo punto emerge il famoso IO di Haskell: non è altro che un modo per essere esplicito" - l'esplicitazione è certamente un vantaggio dell'IO monadico di Haskell, ma c'è un altro punto: fornisce un mezzo per isolare l'effetto collaterale per essere interamente al di fuori del linguaggio - sebbene le implementazioni comuni in realtà non lo facciano, principalmente a causa di problemi di efficienza, concettualmente è vero: la monade IO può essere considerata come un modo per restituire un'istruzione in un ambiente completamente al di fuori del Programma Haskell, insieme a un riferimento a una funzione per continuare dopo il completamento.
Jules,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.