Che cos'è un Comonad e come sono utili?


16

Di recente ho rispolverato le mie conoscenze su come funzionano le Monadi. Sono stato anche introdotto al concetto di "Comonad" , che è descritto come il doppio inverso di una monade . Tuttavia, sono impossibile avvolgerci la testa.

Per capire Monads, ho fatto la mia analogia per me stesso:

Le monadi possono essere viste come "un progetto per costruire nastri trasportatori di espressioni".

Per definire una nuova Monade (un nuovo tipo di sistema di nastri trasportatori) è necessario definire:

  1. Un modo per mettere qualcosa su un nastro trasportatore, ad esempio "avviare" un nastro trasportatore. (Conosciuto come unito return)
  2. Un modo per collegare una macchina (un'espressione) che farà parte di un nastro trasportatore a un nastro trasportatore. (Conosciuto come joino bindo >>=).

(Esiste una terza operazione che prende l'attuale nastro trasportatore, ne getta via il contenuto e avvia un nuovo nastro trasportatore noto come >>, ma viene usato molto raramente.)

Affinché le macchine e i nastri trasportatori funzionino correttamente insieme, è necessario assicurarsi che:

  1. Se si mette qualcosa su un nastro trasportatore e lo si passa attraverso una macchina, l'output dovrebbe essere lo stesso di quando lo si passa attraverso la macchina manualmente. (Identità sinistra)
  2. Se vuoi mettere un nastro trasportatore tra un nastro trasportatore già esistente, non dovresti finire con un nastro trasportatore che ha un nastro trasportatore in cima, ma piuttosto un singolo nastro trasportatore più lungo. (Identità corretta)
  3. Non dovrebbe importare per l'uscita se si utilizza manualmente la macchina A, quindi si passa il risultato attraverso il BC collegato al trasportatore, o se si utilizza AB collegato al trasportatore e quindi si passa il risultato manualmente attraverso C. In altre parole: ((a >> = b) >> = c) dovrebbe essere uguale a (a >> = (b >> = c)) (associatività)

Il nastro trasportatore più semplice sarebbe quello che prende semplicemente l'input e continua sempre con l'espressione successiva. Questa è una "pipeline".

Un'altra possibilità è quella di lasciarlo passare attraverso la macchina successiva solo se viene soddisfatta una condizione per il valore. Ciò significa che se in alcune delle espressioni intermedie, il valore cambia in qualcosa che non è più consentito, il resto delle espressioni verrà saltato. Questo è ciò che fa la monade "Forse" a Haskell.

Puoi anche fare altre fantasiose regole condizionali di copia / modifica sui valori prima o dopo averli passati a una macchina. Un esempio: parser (qui, se un'espressione restituisce un risultato 'fallito', il valore precedente all'espressione viene usato come output).

Naturalmente l'analogia non è perfetta, ma spero che dia una buona rappresentazione di come funzionano le monadi.

Tuttavia, ho molti problemi a capovolgere questa analogia per capire Comonads. So dalle piccole quantità di informazioni che ho trovato su Internet che un Comonad definisce:

  • extract, che è una specie del contrario di return, cioè prende un valore da una Comonad.
  • duplicate, che è una sorta di inverso di join, cioè crea due Comonadi da un singolo.

Ma come si può creare un'istanza di Comonad se siamo in grado di estrarre da loro o duplicarli? E come possono essere effettivamente utilizzati? Ho visto questo fantastico progetto e ne ho parlato (di cui ho purtroppo capito molto poco), ma non sono sicuro di quale parte della funzionalità sia fornita esattamente da un Comonad.

Cos'è una Comonad? A cosa servono? Come possono essere usati? Sono commestibili?


2
"come si può creare un'istanza di Comonad se siamo in grado di estrarre da loro o duplicarli?" - Risponderò alla tua domanda con una domanda: come si può consumare una Monade se si è in grado di elevare i valori in essi e eseguire il calcolo delle sequenze?
Benjamin Hodgson,

1
La "macchina alla fine del nastro trasportatore" (a parte: non trovo le analogie tanto utili quando si parla di monadi) della IOmonade è il sistema di runtime Haskell, che invoca main. C'è anche unsafePerformIO, ovviamente. Se vuoi pensare alla Maybemonade come se avesse una "macchina all'estremità del nastro trasportatore" che puoi usare maybe.
Benjamin Hodgson,

1
Ma, girando la tua spiegazione, quando vuoi produrre un valore comonadico all'inizio di una catena di cobindapplicazioni, ci deve essere una funzione che fa qualcosa di utile con la rappresentazione interna del tuo comonad.
Benjamin Hodgson,

2
istanza specifica di comonad o monad può chiaramente avere più funzionalità del necessario solo per implementare i typeclasses
jk.

2
Non che questo sarà utile se non ti avvicini a questa domanda dal lato teorico / matematico di categoria, ma volevo sottolineare che una comonade non è l'inverso ma piuttosto il doppio di una monade.
Jörg W Mittag,

Risposte:


11

Una comonade è, proprio come una monade, una struttura matematica nella teoria delle categorie. Il co-prefisso è molto comune lì per indicare "inversioni" come dici tu (anche se non credo che i puri matematici siano d'accordo sulla scelta della parola).

Nella teoria delle categorie ci sono categories , che sono brevemente messi una raccolta di objects(di qualsiasi tipo o natura, la struttura interna è irrilevante) e alcuni arrowstra questi oggetti. Affinché qualcosa sia una categoria, le frecce devono seguire alcune leggi (identità sinistra / destra e associatività), ma qui non è molto importante.

Ora, la teoria delle categorie è molto astratta / difficile da comprendere e vasta. Ci vuole molto tempo per passare attraverso tutto (e non l'ho studiato formalmente, conosco solo alcune nozioni di base), ma esiste una nozione che si chiama undual . Fondamentalmente, per ogni categoria puoi costruirne una opposite categorysemplicemente facendo la stessa cosa ma "invertendo tutte le frecce". Questa è una definizione molto ingenua, ma è difficile provare a riassumere. Il doppio di qualcosa in una categoria C è sostanzialmente la stessa cosa nella categoria opposta C_op (hai ancora mal di testa?)

Ad ogni modo, se hai una monade su una categoria (e una categoria può ad esempio essere una categoria in cui gli oggetti sono tipi in un linguaggio di programmazione e le frecce sono funzioni tra i tipi), allora un comonad è sostanzialmente la stessa cosa, solo tu ho invertito tutte le frecce (un po 'come invertire le firme delle funzioni in questo caso).

Una descrizione più "pratica" (anche se non SUPER pratica) può essere trovata in questa discussione tra Erik Meijer e Brian Beckman in cui discutono del concetto di dualità e di come Erik ha fatto "invertire le frecce" per IEnumerable<T>in C # quando creando il framework reattivo e IObservable<T>(che per quanto posso dire, e sono felice di essere corretto, fondamentalmente è un esempio di elenco comune).

Un altro esempio pratico di comonad menzionato nel video è il Task<T>tipo in .NET, dove Task<U> ContinueWith<U>(Func<Task<T>, U>)sarebbe il doppio di bind(o SelectManycome viene chiamato in C #)


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.