2012-03-01 9 views
16

Ultimamente ho scritto codice FFI che restituisce una struttura dati nella monade IO. Per esempio:Ciò che è preferito tra liftM, lilftA, ecc.

peek p = Vec3 <$> (#peek aiVector3D, x) p 
       <*> (#peek aiVector3D, y) p 
       <*> (#peek aiVector3D, z) p 

Ora mi viene in mente quattro modi piacevoli per scrivere che il codice, tutti strettamente correlati:

peek p = Vec3 <$> io1 <*> io2 <*> io3 
peek p = liftA3 Vec3 io1 io2 io3 
peek p = return Vec3 `ap` io1 `ap` io2 `ap` io3 
peek p = liftM3 Vec3 io1 io2 io3 

Si noti che sto chiedendo di codice monade che non richiede nulla al di là cosa fornisce Applicative. Qual è il modo preferito per scrivere questo codice? Devo usare Applicative per sottolineare cosa fa il codice, o dovrei usare Monad perché potrebbe (?) Avere ottimizzazioni su Applicative?

La domanda è un po 'complicata dal fatto che ci sono solo [liftA..liftA3] e [liftM..liftM5] ma ho diversi record con più di tre o cinque membri, quindi se decido di andare con lift{A,M} ho perdere un po' di coerenza, perché avrei dovuto usare un metodo diverso per i record più grandi.

+2

La coerenza indica "ap" o "<*>". Quale scegli tu sia in gran parte una questione di gusti. La mia preferenza (e ho l'impressione che non sia solo mia) è "<*>". –

risposta

25

La prima cosa da ricordare è che questo è un po 'più complicato di quanto dovrebbe essere - qualsiasi Monad esempio dovrebbe avere un associato Applicative esempio in modo tale che le liftM e liftA funzioni coincidono. Come tale, ecco due linee guida:

  • Se state scrivendo una funzione generica per qualsiasiMonad, uso liftM & co. per evitare l'incompatibilità con altre funzioni che hanno solo un vincolo Monad.

  • Se si lavora con una specifica Monad istanza che si sa ha un accompagnamento Applicative esempio, utilizzare Applicative operatori costantemente per qualsiasi definizione o sottoespressione in cui non hai bisogno di Monad operazioni, ma evitare di mescolare senza meta.

Devo usare Applicative sottolineare ciò che il codice fa, o dovrei usare Monad perché potrebbe (?) Hanno ottimizzazioni nel corso Applicative?

In generale, se c'è una differenza, sarà il contrario. Applicative supporta solo una "struttura" statica del calcolo, mentre Monad consente il flusso di controllo incorporato. Considera gli elenchi, ad esempio, con Applicative, tutto ciò che puoi fare è generare tutte le combinazioni possibili e trasformarle ciascuna: il numero di elementi di risultato è determinato interamente dal numero di elementi in ciascun input. Con Monad, è possibile generare diversi numeri di elementi in ogni fase in base agli elementi di input, consentendo di filtrare o espandere arbitrariamente.

Un esempio più potente è è il Applicative e Monad istanze basate su zippare infinita streams-- Applicative può semplicemente zip insieme in modo ovvio, mentre Monad deve ricalcolare un sacco di roba che poi butta via.

Quindi, il numero finale è di liftA2 f x y vs.f <$> x <*> y o gli equivalenti Monad. Il mio consiglio qui sarebbe le seguenti linee guida:

  • Se si scrive ogni argomento fuori comunque, utilizzare il modulo infissa, perché è più facile da leggere per le grandi espressioni.
  • Se si sta semplicemente sollevando una funzione esistente, utilizzare foo = liftA2 bar anziché foo x y = bar <$> x <*> y - è più breve e più chiaramente esprime ciò che si sta facendo.

E, infine, sulla questione della coerenza, non c'è ragione per cui non può semplicemente definire un proprio liftA4 e così via, se ne avete bisogno.

+1

In realtà, penso che 'liftM *' potrebbe essere leggermente più veloce perché usano un numero minore di legami rispetto agli operatori 'Applicative' quando il tipico' puro = id; (<*>) = viene utilizzata l'istanza di ap'. Ma la differenza, se esiste, dovrebbe essere trascurabile. – ehird

+0

Non sapevo se ci sarebbe stata una risposta corretta, ma penso che sia così. –

+0

@ehird, come contrappunto a ciò, a volte è possibile implementare istanze applicative più intelligenti sull'ottimizzazione rispetto alle istanze monad. – luqui

Problemi correlati