Ci scusiamo per l'ennesima domanda sugli effetti collaterali di FP +, ma non sono riuscito a trovarne uno esistente che mi abbia risposto.
La mia (limitata) comprensione della programmazione funzionale è che gli effetti di stato / collaterali dovrebbero essere minimizzati e tenuti separati dalla logica senza stato.
Raccolgo anche l'approccio di Haskell a questo, la monade IO, lo realizza avvolgendo le azioni stateful in un container, per l'esecuzione successiva, considerata al di fuori dell'ambito del programma stesso.
Sto cercando di capire questo schema, ma in realtà per determinare se usarlo in un progetto Python, quindi voglio evitare i dettagli di Haskell se possibile.
Esempio grezzo in arrivo.
Se il mio programma converte un file XML in un file JSON:
def main():
xml_data = read_file('input.xml') # impure
json_data = convert(xml_data) # pure
write_file('output.json', json_data) # impure
L'approccio della monade IO non è efficace nel fare questo:
steps = list(
read_file,
convert,
write_file,
)
quindi assolvere se stesso dalla responsabilità non chiamando effettivamente quei passaggi, ma lasciando che l'interprete lo faccia?
O in altri termini, è come scrivere:
def main(): # pure
def inner(): # impure
xml_data = read_file('input.xml')
json_data = convert(xml_data)
write_file('output.json', json_data)
return inner
quindi aspettandomi che qualcun altro chiami inner()
e dica che il tuo lavoro è fatto perché main()
è puro.
L'intero programma finirà per essere contenuto nella monade IO, in pratica.
Quando il codice viene effettivamente eseguito , tutto dopo aver letto il file dipende dallo stato di quel file, quindi soffrirà ancora degli stessi bug relativi allo stato dell'implementazione imperativa, quindi hai effettivamente guadagnato qualcosa, come programmatore che manterrà questo?
Apprezzo totalmente il vantaggio di ridurre e isolare il comportamento con stato, motivo per cui ho strutturato la versione imperativa in questo modo: raccogliere input, fare cose pure, sputare output. Si spera che convert()
possa essere completamente puro e raccogliere i benefici di cachability, thread-safety, ecc.
Apprezzo anche che i tipi monadici possano essere utili, specialmente nelle condotte che operano su tipi comparabili, ma non vedo perché l'IO dovrebbe usare le monadi se non già in una tale conduttura.
C'è qualche ulteriore vantaggio nel gestire gli effetti collaterali che porta il modello di monade IO, che mi manca?
main
in un programma Haskell è IO ()
- un'azione IO. Questa in realtà non è affatto una funzione; è un valore . L'intero programma è un valore puro contenente istruzioni che indicano al runtime della lingua cosa dovrebbe fare. Tutte le cose impure (che eseguono effettivamente le azioni IO) non rientrano nell'ambito del programma.
read_file
) e usarlo come argomento per quello successivo ( write_file
). Se avessi solo una sequenza di azioni indipendenti, non avresti bisogno di una Monade.