FP: Catturare le caratteristiche di un processo che blocca, causa effetti collaterali e può fallire

0

Ho una funzione driver modifyFile che interagisce con molte fonti nel mondo esterno (ad esempio HTTP, filesystem). Diciamo che il codice è così:

def downloadFile(from: String, to: String): Try[Unit]

def runUnixProcess(cmd: String): Boolean

def modifyFile = {
    downloadFile(from = "http://.../some/file.dat", to = "/tmp/file.dat") // 
    val status = runUnixProcess("/usr/bin/somebinaries /tmp/file.dat")
    if (status == Status.OK) deleteFile("/tmp/file.dat")
}

Per il gusto dell'argomento, consideriamo che queste funzioni rispettano la regola del Principio della singola responsabilità e che modifyFile è solo un driver che "integra" e facilita la comunicazione tra entità separate come OS, File system e web servizio.

Sebbene questa funzione si collochi molto probabilmente al livello più alto della logica dell'applicazione, su di essa potrebbe essere costruita un'altra funzione applicativa. Quindi, voglio avere una sorta di astrazione appropriata (ad esempio avvolgendola con un costrutto monadico) in modo che possiamo ancora comporre un'astrazione ancora più alta su di essa.

Ora la mia domanda: Qual è la migliore costruzione del linguaggio per astrarre questo tipo di calcolo ? Vale a dire un calcolo che:

  • chiama a molte entità nel mondo esterno
  • potrebbe non riuscire, parzialmente o completamente in alcuni passaggi a causa di varie condizioni. Per citarne alcuni, potremmo avere a che fare con il problema del permesso del file system, il problema della connessione di rete, i file binari unix mancanti, ecc.
  • potrebbe dover essere eseguito in modo asincrono, considerando che ci sono diverse chiamate di blocco in esso.

È lo% di% su% di Scalaz per esso?

O dovrei semplicemente racchiuderlo come Task , che sostanzialmente cattura la maggior parte delle caratteristiche che esibisce?

    
posta lolski 26.03.2015 - 12:53
fonte

1 risposta

2

Non sono sicuro di quanto sarebbe facile implementarlo in Scala, ma ecco come vorrei strutturarlo in Haskell:

Per iniziare esaminerò un paio delle monade fornite da Haskell e F #: probabilmente ci sono familiari, ma le esaminerò solo per preparare il palco.

In Haskell esiste il monade IO che viene utilizzato per eseguire operazioni IO (ad esempio chiamate Web, accesso al file system) e che mantiene gli effetti collaterali fuori dal codice puro.

Ci sono anche le forse e le o monadi; queste monadi cortocircuiteranno una catena di funzioni monadiche quando c'è un errore / fallimento in una delle funzioni, bypassando le restanti funzioni nella catena.

In F # c'è il monad Async (alias workflow / computation expression) - Sono sicuro che ci sia un equivalente in Haskell, ma non ho ancora lavorato con uno - il che ti permette di eseguire operazioni asincrone con codice simile a chiamate sequenziali regolari. (Ecco un tutorial introduttivo sulla programmazione asincrona in F # , e c'è un altro tutorial su quel sito che va più nello sviluppo del flusso di lavoro e in che modo bind deriva naturalmente dai callback, ma al momento non riesco a trovarlo.)

Ora, quello che stai chiedendo è un modo per usarli tutti insieme. C'è un modo per farlo ed è un po 'più complesso, ma è sicuramente una cosa carina da avere nella tua cassetta degli attrezzi.

Al di là delle monadi, ci sono i trasformatori monad ; questi ti permettono di comporre le monadi insieme, creando una sorta di super monade che ti permette di usare tutte le capacità delle monadi che la compongono - pensalo come una specie di pila monad dove puoi accedere alle singole monadi a diversi livelli (usando la funzione lift ).

Alcuni esempi (restituisci) le firme dei tipi:

IO (Either String a) -- Either & IO
IO (Async (Maybe a)) -- Maybe, Async, IO

Se, ad esempio, hai un sacco di operazioni di I / O che vuoi eseguire sequenzialmente, ognuna delle quali potrebbe fallire, probabilmente creerai uno stack di trasformatori usando l'IO e l'una o l'altra delle due.

Non approfondirò qui i dettagli, ma il documento Monad Transformers Step-by -Step è una risorsa introduttiva piuttosto buona.

I trasformatori Monad hanno, IMO, una curva di apprendimento ripida, quindi se hai fretta di implementare qualcosa, probabilmente questa non è la strada da percorrere, ma forse ti darà qualche idea. Se stai cercando un'astrazione generale di lunga durata, probabilmente dovresti considerare i trasformatori monad, nonostante il grande investimento iniziale.

    
risposta data 26.03.2015 - 16:39
fonte

Leggi altre domande sui tag