2012-04-24 17 views
20

il tipo di FMAP in Functor è:confuso circa funzione di istanza di Functor in Haskell

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

sembra, la funzione (a -> b) si applicano prima al parametro di fa per creare un risultato di tipo b, quindi applicare f ad esso, e il risultato è fb

utilizzando Forse un esempio:

fmap show (Just 1) 
result is : Just "1" 

equivale a dire:

Just (show 1) 

ma quando (->) è usato come un funtore (in Control.Monad.Instances)

import Control.Monad.Instances 
(fmap show Just) 1 
result is : "Just 1" 

che è, solo è applicare prima, poi spettacolo viene applicato. in un altro esempio, risultato è lo stesso:

fmap (*3) (+100) 1 
result is 303 

perché non * 3, poi 100?

risposta

22

il tipo di FMAP in Functor è:

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

sembra, la funzione si applicano prima (a -> b) al parametro di fa per creare un risultato di tipo b, quindi applicare f ad esso e il risultato è fb

Questo è il tipo di fmap, ma la tua interpretazione di ciò che significa questo tipo è sbagliato.

Si presume che f a abbia un parametro e che quel parametro abbia tipo a.

consideri xs :: [a]:

  • Forse xs = [].
  • Forse xs = [x1].
  • Forse xs = [x1, x2].
  • ...

Il tipo f a è un funtore f con un unico parametro di tipo a. Ma i valori di tipo f a non hanno necessariamente il formato F x, come puoi vedere dal primo e dal terzo caso sopra.

Ora consideriamo fmap f xs:

  • Forse fmap f xs = [].
  • Forse fmap f xs = [f x1].
  • Forse fmap f xs = [f x1, f x2].
  • ...

Noi non applichiamo necessariamente f affatto (primo caso)! Oppure potremmo applicarlo più di una volta (terzo caso).

Quello che facciamo è sostituire le cose di tipo a, con cose di tipo b. Ma lasciamo intatta la struttura più grande --- nessun nuovo elemento aggiunto, nessun elemento rimosso, il loro ordine è rimasto invariato.


Ora pensiamo al funtore (c ->). (Ricordate, un functor accetta solo un parametro di tipo, quindi l'input a (->) è fisso.)

Un c -> a contiene addirittura a? Potrebbe non contenere alcun a s, ma può in qualche modo magia uno fuori dal nulla quando gli diamo un c. Ma il risultato di fmap è di tipo c -> b: dobbiamo solo fornire uno b quando ci viene presentato un c.

Quindi possiamo dire fmap f x = \y -> f (x y).

In questo caso, stiamo applicando f su richiesta --- ogni volta che viene applicata la funzione che restituiamo, viene applicato anche f.

+1

sì, la tua risposta è fantastica! Ho fatto un grosso errore. Grazie mille. –

+0

Confondo "parametro tipo" con un parametro concreto –

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

Ricordare che f può anche essere un costruttore di tipi. Prendo questo per dire che (per i casi più semplici) quella funzione prende qualcosa di tipo a avvolto in un f e lo converte in qualcosa di tipo b avvolto in un f usando la funzione a -> b.

Nel secondo esempio, si sta eseguendo (fmap show Just) 1. Questo è di tipo

Prelude> :t fmap show Just 
fmap show Just :: (Show a, Functor ((->) a)) => a -> String 

Ciò è in contrasto con la precedente

Prelude> :t fmap show (Just 1) 
fmap show (Just 1) :: Maybe String 

La differenza è che nel primo Just è un costruttore di tipo, che Just 1 è un'istanza di un tipo. fmap è opportunamente generico tale da avere significati per entrambi.

+1

In "Ricorda che f può anche essere un costruttore di tipi" si intende probabilmente "deve" invece "può anche". –

+0

'Just' non è un costruttore di tipi, è un valore o un costruttore di dati. 'Maybe' è un costruttore di tipi. –

5

fmap per (->) è definito come fmap = (.). Quindi, (fmap f g) x è (f . g) x è f (g x). Nel tuo caso (*3) ((+100) 1), che equivale a 3 * (100 + 1) che risulta in 303.

26

L'istanza fmap per (->) r (vale a dire le funzioni) è letteralmente solo composizione. Da the source itself:

instance Functor ((->) r) where 
    fmap = (.) 

Quindi, nel tuo esempio, possiamo semplicemente sostituire fmap con (.), e fare alcune trasformazioni

fmap (*3) (+100) 1 => 
(.) (*3) (+100) 1 => 
(*3) . (+100) $ 1 => -- put (.) infix 
(*3) (1 + 100)  => -- apply (+100) 
(1 + 100) * 3   -- apply (*3) 

Cioè, fmap per le funzioni li compone da destra a sinistra (esattamente come (.), che è ragionevole perché è (.)).

Guardarlo in un altro modo (per (doppia) conferma!), Siamo in grado di utilizzare la firma tipo:

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

-- specialised to the function functor (I've removed the last pair of brackets) 
fmap :: (a -> b) -> (r -> a) -> r -> b 

Quindi, prima ha bisogno di essere trasformato in un valore di tipo a (dalla funzione r -> a) il valore di tipo r (il terzo argomento), in modo che la funzione a -> b può trasformarlo in un valore di tipo b (il risultato).

+0

Grazie, questa è una bella definizione chiara! –

+1

sì, fmap :: (a -> b) -> (r -> a) -> r -> b, questo spiega molto, grazie –

14

E deve essere definito in questo modo per rendere funzionanti i tipi. Come lei ha sottolineato, il tipo di fmap è:

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

Consideriamo il caso in cui il funtore f è ((->) c)

(Nota: che avevamo in realtà piacerebbe scrivere questo come (c ->), vale a dire le funzioni da c, ma Haskell non ci permettono di fare questo.)

Poi f a è in realtà ((->) c a), che equivale a (c -> a), e allo stesso modo per il f b, quindi abbiamo:

fmap :: (a -> b) -> (c -> a) -> (c -> b) 

cioè dobbiamo prendere due funzioni:

  • f :: a -> b
  • g :: c -> a

e costruire una nuova funzione

  • h :: c -> b

Ma c'è un solo modo per farlo: si deve applicare g prima di ottenere qualcosa di tipo a, e quindi applicare f per ottenere qualcosa di tipo b, il che significa che si avere definire

instance Functor ((->) c) where 
    fmap f g = \x -> f (g x) 

o, più brevemente,

instance Functor ((->) c) where 
    fmap = (.) 
0

Per creare un tipo di funzione, sono necessari 2 parametri di tipo per (->), ovvero il tipo di argomento di input singolo e il tipo di ritorno.

Un Functor può prendere solo un parametro di tipo, quindi è necessario definire il tipo di argomento di input (poiché è il primo da sinistra a destra), il che rende il tipo di ritorno della funzione come il parametro type del funtore.

Quindi per funzione (il Functor) a-> b, è necessario assegnare a fmap una funzione ff di tipo b-> xxx diversa da a-> xxx per funzionare, e ciò significa che la funzione ff può essere applicata solo dopo a-> b è applicabile.

Problemi correlati