2015-02-21 14 views
11

Ho letto spesso chePerché è utile Monnal Identity?

Sembra che la monade dell'identità sia inutile. Non è ... ma questo è un altro argomento .

Così qualcuno può dire come è utile?

+0

lo vedo come un grande strumento per spiegare monadi a persone senza precedenti conoscenze su questo genere di cose e come un esercizio prezioso per chiunque impari come implementare le monadi. – ThreeFx

risposta

12

Identity è quello di monadi, funtori e funtori applicative come 0 è di numeri. Di per sé sembra inutile, ma è spesso necessario nei luoghi in cui ci si aspetta una monade o un funtore (applicativo) che in realtà non fa nulla.

Come già menzionato, Identity permette di definire trasformatori solo monad e quindi definire le monadi corrispondenti altrettanto SomeT Identity.

Ma non è tutto. Spesso è anche utile definire altri concetti in termini di monadi, che di solito aggiungono molta flessibilità. Ad esempio Conduit i m o (vedi anche this tutorial) definisce un elemento in una pipeline che può richiedere dati di tipo i, può produrre dati di tipo o e utilizza la tecnologia monad m per l'elaborazione interna. Allora tale tubazione può essere eseguito in monade determinato utilizzando

($$) :: Monad m => Source m a -> Sink a m b -> m b 

(dove Source è un alias per Conduit senza ingresso e Sink per Conduit senza output). E quando non sono necessari calcoli effectful in cantiere, il codice solo pura, abbiamo appena specializziamo m-Identity ed eseguire tale gasdotto come

runIdentity (source $$ sink) 

Identity è anche il funtore "vuoto" e funtore applicativa: Identity composto con un altro funtore o funtore applicativo isomorfo all'originale. Ad esempio, Lens' è definito come un polimorfica funzione in un Functor:

Functor f => (a -> f a) -> s -> f s 

grosso modo, una tale lente permette di leggere o manipolare qualcosa di tipo a all'interno s, ad esempio un campo all'interno di un disco (per un'introduzione agli obiettivi vedere this post). Se specializzati f-Identity, otteniamo

(a -> Identity a) -> s -> Identity s 

che è isomorfo a

(a -> a) -> s -> s 

così data una funzione di aggiornamento sulla a, restituire una funzione di aggiornamento sui s. (Per completezza: Se specializzati f-Const a, otteniamo (a -> Const b a) -> s -> Const b s, che è isomorfo a (a -> b) -> (s -> b), cioè, dato un lettore su a, restituiscono un lettore su s.)

3

Un caso d'uso reale deve essere una base (pura) di stack di trasformatori monad, ad es.

type Reader r = ReaderT r Identity 
9

Un uso di esso è come monade base per monad pile trasformatori: invece di dover prevedere due tipi Some :: * ->* e SomeT :: (* -> *) -> * -> *, è sufficiente fornire solo un secondo di impostazione type Some = SomeT Identity.

Un altro, un po 'simile caso d'uso (ma completamente staccato da tutta la faccenda Monade) è quando è necessario fare riferimento alle tuple: possiamo dire () è una tupla nullaria, (a, b) è una tupla binario, (a, b, c) è una tupla ternario, e così via, ma cosa lascia per caso unario? Dire a è una tupla unaria per qualsiasi scelta di a che spesso non è soddisfacente, ad esempio quando stiamo costruendo alcune istanze di typeclass come Data.Tuple.Select, qualche tipo di costruttore è necessario per fungere da chiave non ambigua. Quindi aggiungendo per es. Sel1 istanze a Identity a, ci costringe a distinguere tra (a, b) (una doppia tupla contenente uno a e uno b) e Identity (a, b) (una tupla singola contenente un singolo valore (a, b)).

(Si noti che Data.Tuple.Select definisce il suo proprio tipo chiamato OneTuple invece di riutilizzare Identity, ma è isomorfo a Identity fatto -in, è solo un rinomina lontano e ritengo esiste solo per evitare una non base dipendenza.)

+1

'Identity' si sta unendo alla base in 4.8 comunque, in modo che possa presto essere un artefatto storico. 'Data.Sequence' usa un tipo' Elem' interno (anche isomorfo a 'Identity') in questi modi. – dfeuer

11

A volte lavoro con i record i cui campi sono facoltativi in ​​alcuni contesti (come quando si analizza il record da JSON) ma obbligatorio in altri.

Io lo risolvo parametrizzando il record con un funtore e usando Maybe o Identity in ciascun caso.

{-# LANGUAGE DeriveGeneriC#-} 
{-# LANGUAGE StandaloneDeriving #-} 

data Query f = Query 
    { 
     _viewName :: String 
    , _target :: f Server -- Server is some type, it doesn't matter which 
    } 
    deriving (Generic) 

campo Server è facoltativo durante l'analisi JSON:

instance FromJSON (Query Maybe) 

Ma poi ho una funzione come

withDefaultServer :: Server -> Query Maybe -> Query Identity 
withDefaultServer = undefined 

che restituisce un record in cui il campo _target è obbligatorio.

(Questa risposta non usa nulla monadica su Identity, però.)