Mi piace pensare ad Arrows, come Monads e Functors, come a consentire al programmatore di fare composizioni esotiche di funzioni.
Senza Monadi o Frecce (e Functor), la composizione delle funzioni in un linguaggio funzionale si limita ad applicare una funzione al risultato di un'altra funzione. Con monadi e funzioni, è possibile definire due funzioni e quindi scrivere un codice riutilizzabile separato che specifichi in che modo tali funzioni, nel contesto di una particolare monade, interagiscono tra loro e con i dati che vi vengono trasmessi. Questo codice è inserito nel codice vincolante della Monade. Quindi una monade è una vista unica, solo un contenitore per il codice bind riutilizzabile. Le funzioni si compongono in modo diverso nel contesto di una monade da un'altra monade.
Un semplice esempio è la monade Maybe, dove esiste un codice nella funzione bind tale che se una funzione A è composta con una funzione B all'interno di una monade Maybe, e B produce un Nothing, il codice bind assicurerà che la composizione del due funzioni generano un Niente, senza preoccuparsi di applicare A al valore Nulla che esce da B. Se non ci fosse monade, il programmatore dovrebbe scrivere il codice in A per testare un input Nulla.
Le monadi significano anche che il programmatore non ha bisogno di digitare esplicitamente i parametri richiesti da ciascuna funzione nel codice sorgente - la funzione bind gestisce il passaggio dei parametri. Quindi, usando le monadi, il codice sorgente può iniziare ad apparire più come una catena statica di nomi di funzioni, piuttosto che sembrare che la funzione A "chiama" la funzione B con i parametri C e D - il codice inizia a sembrare più un circuito elettronico che un macchina in movimento - più funzionale che indispensabile.
Le frecce collegano anche le funzioni con una funzione di associazione, fornendo funzionalità riutilizzabili e nascondendo i parametri. Ma le frecce possono esse stesse essere collegate tra loro e composte e, facoltativamente, possono instradare i dati ad altre frecce in fase di esecuzione. Ora puoi applicare i dati a due percorsi di Frecce, che "fanno cose diverse" ai dati e riassemblare il risultato. Oppure puoi selezionare a quale ramo di Frecce passare i dati, a seconda di un valore nei dati. Il codice risultante è ancora più simile a un circuito elettronico, con interruttori, ritardi, integrazione ecc. Il programma sembra molto statico e non dovresti essere in grado di vedere molta manipolazione dei dati in corso. Ci sono sempre meno parametri a cui pensare e meno bisogno di pensare a quali valori i parametri possono o meno accettare.
La scrittura di un programma Arrowized implica principalmente la selezione di frecce come divisori, interruttori, ritardi e integratori, il sollevamento di funzioni in quelle frecce e il collegamento delle frecce per formare frecce più grandi. Nella Arrowized Functional Reactive Programming, le frecce formano un loop, con l'input dal mondo combinato con l'output dell'ultima iterazione del programma, in modo che l'output reagisca all'input del mondo reale.
Uno dei valori del mondo reale è il tempo. In Yampa, la freccia della funzione del segnale inserisce invisibilmente il parametro time nel programma per computer: non si accede mai al valore del tempo, ma se si collega una freccia dell'integratore al programma, genererà valori integrati nel tempo che è possibile utilizzare per passare a altre frecce.