2010-03-07 11 views
110

Non capisco cosa sia "lifting". Dovrei prima capire le monadi prima di capire cos'è un "ascensore"? (Sono completamente ignorante anche sulle monadi :) O qualcuno può spiegarmelo con parole semplici?Che cos'è il "sollevamento" in Haskell?

+6

Forse utile, forse no: http://www.haskell.org/haskellwiki/Lifting – kennytm

risposta

147

Il sollevamento è più un modello di progettazione che un concetto matematico (anche se mi aspetto che qualcuno qui intorno ora mi smentisca mostrando come gli ascensori sono una categoria o qualcosa del genere).

In genere si dispone di un tipo di dati con un parametro. Qualcosa di simile

data Foo a = Foo { ...stuff here ...} 

Supponiamo si scopre che un sacco di usi di Foo prendere tipi numerici (Int, Double, ecc) e si mantiene dover scrivere codice che scarta questi numeri, aggiunge o li moltiplica, e poi li avvolge indietro su. Puoi cortocircuitarlo scrivendo il codice unwrap-and-wrap una volta. Questa funzione viene tradizionalmente chiamato un "lift", perché assomiglia a questo:

liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c 

In altre parole si dispone di una funzione che prende una funzione a due argomenti (come ad esempio l'operatore (+)) e lo trasforma in funzione equivalente per Foos.

Così ora si può scrivere

addFoo = liftFoo2 (+) 

Edit: ulteriori informazioni

Ovviamente si può avere liftFoo3, liftFoo4 e così via. Tuttavia questo spesso non è necessario.

Inizia con l'osservazione

liftFoo1 :: (a -> b) -> Foo a -> Foo b 

Ma questo è esattamente lo stesso di fmap.Quindi, piuttosto che liftFoo1 si può scrivere

instance Functor Foo where 
    fmap foo = ... 

Se si vuole veramente completa regolarità si può quindi dire che

liftFoo1 = fmap 

Se si può fare Foo in un funtore, forse si può rendere un funtore applicativo. In realtà, se si può scrivere liftFoo2 quindi l'istanza applicativa appare così:

import Control.Applicative 

instance Applicative Foo where 
    pure x = Foo $ ... -- Wrap 'x' inside a Foo. 
    (<*>) = liftFoo2 ($) 

Il (<*>) operatore per Foo ha il tipo

(<*>) :: Foo (a -> b) -> Foo a -> Foo b 

Si applica la funzione avvolto al valore avvolto. Pertanto, se è possibile implementare liftFoo2, è possibile scriverlo in termini di esso. In alternativa è possibile implementare direttamente e non perdere tempo con liftFoo2, poiché il modulo Control.Applicative comprende

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c 

e allo stesso modo ci sono liftA e liftA3. Ma in realtà non li uso molto spesso perché non v'è un altro operatore

(<$>) = fmap 

Questo consente di scrivere:

result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4 

Il termine myFunction <$> arg1 restituisce una nuova funzione avvolto in Foo. Questo a sua volta può essere applicato all'argomento successivo utilizzando (<*>) e così via. Quindi ora invece di avere una funzione di sollevamento per ogni area, hai solo una catena di applicazioni.

+20

Probabilmente vale la pena ricordare che gli ascensori devono rispettare le leggi standard 'lift id == id' e' lift (f. G) == (lift f). (ascensore g) '. –

+11

Gli ascensori sono effettivamente "una categoria o qualcosa". Carlos ha appena elencato le leggi di Functor, dove 'id' e' .' sono la composizione di freccia e freccia di identità di alcune categorie, rispettivamente. Di solito quando si parla di Haskell, la categoria in questione è "Hask", le cui frecce sono funzioni di Haskell (in altre parole, 'id' e' .' si riferiscono alle funzioni di Haskell che conosci e ami). –

+0

Mi piace il modo in cui si inizia citando un tipo con un parametro. Questa è l'idea più importante di Monad. Non riesco ancora a credere che nel libro: haskell del mondo reale, l'autore non ha spiegato che Monad è semplicemente una classe di tipo (controlla il capitolo sulle monadi e la programmazione di monad su quel libro) – osager

10

di sollevamento è un concetto che permette di trasformare una funzione in una funzione corrispondente all'interno di un altro (di solito più generale) impostando

un'occhiata a http://haskell.org/haskellwiki/Lifting

+34

Sì, ma quella pagina inizia "Di solito iniziamo con un (fasatore) covariante ...". Non esattamente newbie friendly. –

+3

Ma "functor" è collegato, quindi il principiante può semplicemente fare clic per vedere che cos'è un Functor. Certo, la pagina collegata non è così buona. Devo avere un account e sistemarlo. – jrockway

+4

È un problema che ho riscontrato su altri siti di programmazione funzionale; ogni concetto è spiegato in termini di altri concetti (non familiari) fino a quando il novizio non completa il cerchio (e attorno alla curva). Deve essere qualcosa a che fare con la simpatia della ricorsione. – DNA

19

Cominciamo con un esempio:

> replicate 3 'a' 
"aaa" 
> :t replicate 
replicate :: Int -> a -> [a] 
> :t liftA2 replicate 
liftA2 replicate :: (Applicative f) => f Int -> f a -> f [a] 
> (liftA2 replicate) [1,2,3] ['a','b','c'] 
["a","b","c","aa","bb","cc","aaa","bbb","ccc"] 
> :t liftA2 
liftA2 :: (Applicative f) => (a -> b -> c) -> (f a -> f b -> f c) 

liftA2 trasforma una funzione di tipi semplici in una funzione di questi tipi racchiusi in un Applicative, ad esempio elenchi, IO, ecc.

Un altro ascensore comune è lift da Control.Monad.Trans. Trasforma un'azione monadica di una monade in un'azione di una monade trasformata.

In generale, gli ascensori "sollevano" una funzione/azione in un tipo "avvolto".

Il modo migliore per capire questo, e le monade ecc. E per capire perché sono utili, è probabilmente quello di codificarlo e usarlo. Se c'è qualcosa che hai codificato in precedenza che ritieni possa trarre beneficio da questo (cioè questo renderà il codice più breve, ecc.), Provalo e afferrerai facilmente il concetto.

34

Paul e yairchu sono entrambe buone spiegazioni.

Vorrei aggiungere che la funzione che si sta sollevando può avere un numero arbitrario di argomenti e che non devono essere dello stesso tipo. Ad esempio, si potrebbe anche definire un liftFoo1:

liftFoo1 :: (a -> b) -> Foo a -> Foo b 

In generale, il sollevamento delle funzioni che richiedono 1 argomento viene catturato nella classe tipo Functor, e l'operazione di sollevamento è chiamato fmap:

fmap :: Functor f => (a -> b) -> f a -> f b 

Notare la somiglianza con il tipo di liftFoo1. Infatti, se si dispone liftFoo1, è possibile effettuare Foo un'istanza di Functor:

instance Functor Foo where 
    fmap = liftFoo1 

Inoltre, la generalizzazione di sollevamento ad un numero arbitrario di argomenti è chiamato stile applicativa. Non preoccuparti di immergerti in questo fino a quando non coglierai le funzioni con un numero fisso di argomenti. Ma quando lo fai, Learn you a Haskell ha un buon capitolo su questo. Il Typeclassopedia è un altro documento valido che descrive Functor e Applicativo (così come altre classi di tipi, scorrere verso il basso nel capitolo a destra in quel documento).

Spero che questo aiuti!

0

Secondo this shiny tutorial, un funtore è qualche contenitore (come Maybe<a>, List<a> o Tree<a> che può memorizzare elementi di qualche altro tipo, a). Ho usato la notazione Java generica, <a>, per il tipo di elemento a e penso agli elementi come bacche sull'albero Tree<a>. Esiste una funzione fmap, che accetta una funzione di conversione degli elementi, a->b e il contenitore functor<a>. Si applica a->b a ogni elemento del contenitore convertendolo in modo efficace in functor<b>. Quando viene fornito solo il primo argomento, a->b, fmap attende lo functor<a>. Cioè, fornire a->b da solo trasforma questa funzione a livello di elemento nella funzione functor<a> -> functor<b> che opera sui contenitori. Questo è chiamato sollevamento della funzione. Poiché il contenitore è anche chiamato un functor, i Functional piuttosto che Monads sono un prerequisito per il sollevamento. Le monadi sono un po '"parallele" al sollevamento. Entrambi si basano sulla nozione di Functor e fanno f<a> -> f<b>. La differenza è che il sollevamento utilizza a->b per la conversione mentre Monad richiede all'utente di definire a -> f<b>.

+4

Ti ho dato un segno di spunta, perché "un funtore è un contenitore" è un'esca infuocata alla troll. Esempio: funzioni da alcuni 'r' a un tipo (usiamo' c' per varietà), sono Functors. Non "contengono" alcun 'c'. In questo caso, fmap è una composizione di funzioni, che utilizza una funzione 'a -> b' e una' r -> a', per darti una nuova funzione 'r -> b'. Ancora nessun contenitore. Inoltre, se potessi, lo annoterei di nuovo per la frase finale. – BMeph

+1

Inoltre, 'fmap' è una funzione, e non" aspetta "nulla; Il "contenitore" essendo un Functor è l'intero punto di sollevamento. Inoltre, le Monade sono, semmai, una doppia idea di sollevamento: una Monade ti consente di usare qualcosa che è stato sollevato un numero positivo di volte, come se fosse stato sollevato una sola volta - questo è meglio conosciuto come _flattening_. – BMeph

+1

@BMeph "Aspettare", "aspettarsi", "anticipare" sono i sinonimi. Dicendo "la funzione aspetta" intendevo "la funzione anticipa". – Val

Problemi correlati