Nel mio caso d'uso, ha senso fare in modo che ogni metodo principale restituisca un risultato asincrono?

1

Ho un'applicazione Playframework scritta in Scala. Come ci si potrebbe aspettare, la sua funzione primaria è rispondere alle richieste degli utenti. Flusso di richiesta tipico:

  • Analizza l'input e capisce cosa l'utente vuole che noi facciamo
  • Raccogliere risorse esterne da db, AWS e così via (chiamate IO asincrone relativamente lente).
  • Quando ritornano le chiamate IO, esegui una serie di funzioni (alcune delle quali di lunga durata, alcune di loro no), che fanno tutto ciò che l'utente ha richiesto, producendo in ultima analisi un output.
  • Traduci l'output in una risposta HTTP e restituiscilo all'utente.

Quali sono i lati negativi di avere sempre importanti funzioni di ritorno Future [SomeResult], anche quando la funzione non esegue lavori di lunga durata? (In questi casi utilizzeremmo Future.successful o equivalente per elevare il valore in un Future.)

Un lato positivo è che semplifica la composizione di tutto + otteniamo il piacevole zucchero sintattico per la comprensione.

Uno svantaggio che posso vedere è che le firme delle funzioni stanno "mentendo" - suggeriscono che la funzione esegue un'operazione asincrona, ma in molti casi non lo fa. Ciò significa che dovresti effettivamente guardare la funzione per vedere cosa sta succedendo.

    
posta FullTimeCoderPartTimeSysAdmin 08.06.2017 - 01:06
fonte

2 risposte

3

One downside I can see is that the function signatures are "lying"

Questo è in realtà abbastanza significativo. Le firme dei tipi sono una documentazione aggiornata e garantita sul tuo sistema. Avere firme di tipo non rappresentativo è come avere una documentazione ingannevole: danneggia i tempi di accelerazione e i costi di sviluppo futuri.

Inoltre, mettere Future.successful nelle tue funzioni le rende effettivamente meno componibili. Il sollevamento e la mappatura sono parti centrali della programmazione funzionale e sono facili da fare: invertire un sollevamento quando è necessario, anche se non è qualcosa di generalmente facile da fare.

Per questi motivi eviterei questo approccio.

    
risposta data 08.06.2017 - 01:22
fonte
2

Suggerirei di non utilizzare Future a meno che non sia necessario. Ad esempio, la tua interfaccia con AWS avrebbe senso avere tutti i metodi di restituzione Future . La complessità arriva quando hai un'interfaccia che può avere implementazioni di lunga durata o non può. In questi casi, puoi astrarre il tipo restituito (ad esempio, type-class), utilizzare semplicemente Future per tutti, oppure utilizzare un'interfaccia sincrona e lasciarlo al chiamante per il parallelismo. Usare Future è abbastanza facile da implementare, ma IMO rende le cose più difficili da ragionare. Inoltre ti accoppia ai futures di Scala-- che hanno alcuni aspetti negativi .

Suggerirei di consultare monade gratuite perché possono aiutare ad affrontare questi problemi. Ad esempio, puoi definire un monad AwsOp gratuito ed esprimere la logica AWS con le comprensioni su una monade libera. Quindi, quando si interpreta la monade libera, si può scegliere di usare Future , fs.Task , o qualsiasi monade di effetti collaterali che si desidera, oppure si può interpretare il monade libero in modo sincrono o una combinazione. Il vantaggio qui è che la tua API è espressa in free-monad, e il suo fino all'interprete se Future è usato o meno.

    
risposta data 08.06.2017 - 01:19
fonte

Leggi altre domande sui tag