Come funziona la monade libera e le estensioni reattive?

14

Vengo da uno sfondo C #, in cui LINQ si è evoluto in Rx.NET, ma ha sempre avuto un certo interesse per FP. Dopo qualche introduzione alle monadi e alcuni progetti paralleli in F #, ero pronto a provare a passare al livello successivo.

Ora, dopo diversi discorsi sulla monade libera da parte di persone di Scala e più riscrizioni in Haskell o F #, ho trovato grammatiche con gli interpreti per la comprensione abbastanza simili a IObservable chains.

In FRP si compone una definizione di operazione da blocchi di dominio più piccoli, inclusi gli effetti collaterali e gli errori che rimangono all'interno della catena, e si modella l'applicazione come un insieme di operazioni ed effetti collaterali. Nella monade libera, se ho capito bene, fai lo stesso facendo le tue operazioni come funtori e sollevandoli usando il coyoneda.

Quali sarebbero le differenze tra entrambi che inclinano l'ago verso uno qualsiasi degli approcci? Qual è la differenza fondamentale nella definizione del tuo servizio o programma?

    
posta MLProgrammer-CiM 06.06.2016 - 23:54
fonte

1 risposta

5

Monadi

Un monad consiste di

  • Un endofunctor . Nel nostro mondo dell'ingegneria del software, possiamo dire che questo corrisponde a un tipo di dati con un parametro di tipo unico, non limitato. In C #, questo sarebbe qualcosa di forma:

    class M<T> { ... }
    
  • Due operazioni definite su quel tipo di dati:

    • return / pure prende un valore "puro" (cioè, un valore T ) e "lo avvolge" nella monade (cioè produce un valore M<T> ). Poiché return è una parola chiave riservata in C #, userò pure per fare riferimento a questa operazione da ora in poi. In C #, pure sarebbe un metodo con una firma come:

      M<T> pure(T v);
      
    • bind / flatmap prende un valore monadico ( M<A> ) e una funzione f . f prende un valore puro e restituisce un valore monadico ( M<B> ). Da questi, bind produce un nuovo valore monadico ( M<B> ). bind ha la seguente firma C #:

      M<B> bind(M<A> mv, Func<A, M<B>> f);
      

Inoltre, per essere una monade, sono necessari pure e bind per obbedire alle tre leggi monad.

Ora, un modo per modellare le monadi in C # sarebbe quello di costruire un'interfaccia:

interface Monad<M> {
  M<T> pure(T v);
  M<B> bind(M<A> mv, Func<A, M<B>> f);
}

(Nota: per mantenere le cose brevi ed espressive, mi prenderò delle libertà con il codice in tutta questa risposta.)

Ora possiamo implementare le monadi per i datagypes concreti implementando implementazioni concrete di Monad<M> . Ad esempio, potremmo implementare la seguente monade per IEnumerable :

class IEnumerableM implements Monad<IEnumerable> {
  IEnumerable<T> pure(T v) {
    return (new List<T>(){v}).AsReadOnly();
  }

  IEnumerable<B> bind(IEnumerable<A> mv, Func<A, IEnumerable<B>> f) {
    ;; equivalent to mv.SelectMany(f)
    return (from a in mv
            from b in f(a)
            select b);
  }
}

(Uso intenzionalmente la sintassi LINQ per richiamare la relazione tra sintassi LINQ e monadi, ma nota che potremmo sostituire la query LINQ con una chiamata a SelectMany .)

Ora, possiamo definire una monade per IObservable ? Sembrerebbe di sì:

class IObservableM implements Monad<IObservable> {
  IObservable<T> pure(T v){
    Observable.Return(v);
  }

  IObservable<B> bind(IObservable<A> mv, Func<A, IObservable<B>> f){
    mv.SelectMany(f);
  }
}

Per essere sicuri di avere una monade, dobbiamo dimostrare le leggi della monade. Questo può essere non banale (e non conosco abbastanza bene con Rx.NET per sapere se possono essere provati solo dalle specifiche), ma è un inizio promettente. Per facilitare il resto di questa discussione, supponiamo che le leggi della monade siano valide in questo caso.

Monade gratuite

Non esiste una singolare "monade libera". Piuttosto, le monadi libere sono una classe di monadi costruite dai funtori. Cioè, dato un functor F , possiamo automaticamente derivare una monade per F (cioè, la monade libera di F ).

Funtori

Come le monadi, i funtori possono essere definiti dai seguenti tre elementi:

  • Un tipo di dati, parametrizzato su una singola variabile di tipo non ristretto.
  • Due operazioni:

    • pure avvolge un valore puro nel functor. Questo è analogo a pure per una monade. Infatti, per i funtori che sono anche una monade, i due dovrebbero essere identici.
    • fmap mappa i valori nell'input a nuovi valori nell'output tramite una determinata funzione. La sua firma è:

      F<B> fmap(Func<A, B> f, F<A> fv)
      

Come le monadi, ai funtori è richiesto di obbedire alle leggi del funtore.

Simile alle monadi, possiamo modellare i funtori tramite la seguente interfaccia:

interface Functor<F> {
  F<T> pure(T v);
  F<B> fmap(Func<A, B> f, F<A> fv);
}

Ora, dal momento che le monadi sono una sottoclasse di funtori, potremmo anche refactificare Monad un po ':

interface Monad<M> extends Functor<M> {
  M<T> join(M<M<T>> mmv) {
    Func<T, T> identity = (x => x);
    return mmv.bind(x => x); // identity function
  }

  M<B> bind(M<A> mv, Func<A, M<B>> f) {
    join(fmap(f, mv));
  }
}

Qui ho aggiunto un metodo aggiuntivo, join e fornito implementazioni predefinite di join e bind . Si noti, tuttavia, che queste sono definizioni circolari. Quindi dovresti scavalcare almeno l'uno o l'altro. Inoltre, nota che pure è ora ereditato da Functor .

IObservable e Monade gratuite

Ora, poiché abbiamo definito una monade per IObservable e poiché le monadi sono una sottoclasse di funtori, ne consegue che dobbiamo essere in grado di definire un'istanza di functor per IObservable . Ecco una definizione:

class IObservableF implements Functor<IObservable> {
  IObservable<T> pure(T v) {
    return Observable.Return(v);
  }

  IObservable<B> fmap(Func<A, B> f, IObservable<A> fv){
    return fv.Select(f);
  }
}

Ora che abbiamo un functor definito per IObservable , possiamo costruire una monade libera da quel functor. E questo è esattamente il modo in cui IObservable si riferisce alle monadi libere, vale a dire che possiamo costruire una monade libera da IObservable .

    
risposta data 07.09.2017 - 00:29
fonte

Leggi altre domande sui tag