2014-08-28 18 views
10

Esiste un modo per eliminare la sofferenza dalla semplificazione dell'espressione?Apprendimento Haskell - Come semplificare le espressioni?

Ad esempio, dato questa espressione:

(+) <$> a <*> b $ 1 

Mi piacerebbe vedere uno strumento che potrebbe spiegare che cosa significa. E 'abbastanza laboriosa per i principianti (che trovano definizione corretta funzione di esempio nelle fonti, controllando precedenza degli operatori) per semplificare le espressioni con tutti i passi da seguire:

fmap (+) a <*> b $ 1 

Vedi definition in Data.Functor

(.) (+) a <*> b $ 1 

Vedi fmap in Control.Monad.Instances per instance Functor ((->) r)

e così via.

EDIT: Per chiarire, io sto cercando un modo per riscrivere l'espressione utilizzando le definizioni attuali operazioni in modo che potesse capire nuovo arrivato l'esito di questa espressione. Come dire che (<$>) = fmap qui? Non so come trovare una particolare definizione di istanza (sorgente) usando hoogle e altri strumenti.

MODIFICA: È stata modificata l'espressione originale errata per corrispondere alle seguenti riduzioni.

risposta

3

Inizio ghci, :cd alla directory di base della sorgente che si sta leggendo, :load il modulo che ti interessa, e utilizza il comando :i per avere le informazioni:

ghci> :i <$> 
(<$>) :: Functor f => (a -> b) -> f a -> f b 
    -- Defined in `Data.Functor' 
infixl 4 <$> 
ghci> :i $ 
($) :: (a -> b) -> a -> b -- Defined in `GHC.Base' 
infixr 0 $ 
ghci> :i . 
(.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in `GHC.Base' 
infixr 9 . 

Che la dice digita, dove è definito, l'associatività (infixl o infixr) e la precedenza (il numero più alto è più stretto). Quindi (*10) <$> a $ 1 viene letto come ((*10) <$> a) $ 1.

Quando si un modulo :load, tutti i nomi nell'ambito di questo modulo saranno inclusi nell'ambito ghci. L'unico punto in cui questo può risultare fastidioso è che se si ha un errore nel codice, allora non è possibile fare il :i all'interno di esso. In questi casi, puoi commentare le righe, usare undefined e probabilmente usare anche i buchi digitati come suggerisce behlkir (non hai ancora giocato con quelli troppo).

Mentre ci sei, prova il comando :? in ghci.

6

trovo che il modo più semplice è quello di utilizzare typed holes disponibile in GHCi 7.8:

> (*10) <$> _a $ 1 
Found hole ‘_a’ with type: s0 -> b 
Where: ‘s0’ is an ambiguous type variable 
     ‘b’ is a rigid type variable bound by 
      the inferred type of it :: b at <interactive>:4:1 
Relevant bindings include it :: b (bound at <interactive>:4:1) 
In the second argument of ‘(<$>)’, namely ‘_a’ 
In the expression: (* 10) <$> _a 
In the expression: (* 10) <$> _a $ 1 

Quindi questo mi dice che a :: s0 -> b. Successivo è quello di capire l'ordine degli operatori:

> :i (<$>) 
(<$>) :: Functor f => (a -> b) -> f a -> f b 
infixl 4 <$> 
> :i ($) 
($) :: (a -> b) -> a -> b 
infixr 0 $ 

Quindi questo dice che $ è altamente destra-associative, e dato il suo tipo si vede che è primo argomento deve essere una funzione, in modo da a deve essere una funzione (doppia conferma). Ciò significa che lo (*10) <$> a $ 1 è lo stesso di ((*10) <$> a) $ 1, quindi ci concentreremo prima su (*10) <$> a.

> :t ((*10) <$>) 
((*10) <$>) :: (Num a, Functor f) => f a -> f a 
> :t (<$> _a) 
Found hole ‘_a’ with type: f a 
Where: ‘a’ is a rigid type variable bound by 
      the inferred type of it :: (a -> b) -> f b at Top level 
     ‘f’ is a rigid type variable bound by 
      the inferred type of it :: (a -> b) -> f b at Top level 
In the second argument of ‘(<$>)’, namely ‘_a’ 
In the expression: (<$> _a) 

così abbiamo bisogno a di essere un funtore. Quali sono le istanze disponibili?

> :i Functor 
class Functor (f :: * -> *) where 
    fmap :: (a -> b) -> f a -> f b 
    (<$) :: a -> f b -> f a 
     -- Defined in ‘GHC.Base’ 
instance Functor Maybe -- Defined in ‘Data.Maybe’ 
instance Functor (Either a) -- Defined in ‘Data.Either’ 
instance Functor ZipList -- Defined in ‘Control.Applicative’ 
instance Monad m => Functor (WrappedMonad m) 
    -- Defined in ‘Control.Applicative’ 
instance Control.Arrow.Arrow a => Functor (WrappedArrow a b) 
    -- Defined in ‘Control.Applicative’ 
instance Functor (Const m) -- Defined in ‘Control.Applicative’ 
instance Functor [] -- Defined in ‘GHC.Base’ 
instance Functor IO -- Defined in ‘GHC.Base’ 
instance Functor ((->) r) -- Defined in ‘GHC.Base’ 
instance Functor ((,) a) -- Defined in ‘GHC.Base’ 

Così (->) r capita di essere uno, che è impressionante perché sappiamo a deve essere una funzione. Dal vincolo Num, è possibile determinare che r deve essere uguale a Num a => a. Ciò significa che (*10) <$> a :: Num a => a -> a. Da quel momento applicheremo 1 ad esso, e otterremmo (*10) <$> a $ 1 :: Num a, dove a è una funzione sconosciuta.

Tutto questo è rilevabile tramite GHCi utilizzando :t e :i insieme a fori digitati. Certo, c'è un bel numero di passaggi coinvolti, ma non fallisce mai quando stai cercando di scomporre un'espressione complessa, basta guardare i tipi di diverse sotto-espressioni.

5

GHCi è stato suggerito meravigliosamente e correttamente, e lo suggerisco anche io.

voglio anche suggerire Hoogle, perché con la ricerca istantanea abilitato (alla barra laterale in alto a destra c'è un pulsante per esso), è possibile cercare le funzioni molto rapidamente, può fornire molto, molto maggiori informazioni di GHCi, e la parte migliore è che non devi menzionare i moduli per cercare in loro . Questo è in contrasto con GHCi dove si deve importare prima:

ghci> :t pure 
<interactive>:1:1: Not in scope: ‘pure’ 
ghci> :m +Control.Applicative 
ghci> :t pure 
pure :: Applicative f => a -> f a 

Il link Hoogle sopra è solo uno (dal sito Haskell.org). Hoogle è un programma che puoi installare anche sulla tua macchina (cabal install hoogle) ed eseguire query dalla riga di comando (hoogle your-query).
Sidenote: è necessario eseguire hoogle data per raccogliere prima le informazioni. Richiede wget/curl, quindi se sei su Windows probabilmente dovresti ottenere this nel tuo percorso prima (o un arricciamento per Windows, ovviamente). Su Linux è quasi sempre integrato (se non lo hai su Linux, solo apt-get). A proposito, non uso mai Hoogle dalla riga di comando, semplicemente non è così accessibile, ma può comunque essere molto utile perché alcuni editor di testo e i relativi plug-in possono trarne vantaggio.

In alternativa è possibile utilizzare FPComplete's Hoogle che a volte è più soddisfacente (perché nella mia esperienza è stato a conoscenza di più librerie di terze parti. Uso solo in quelle "sessioni di Hoogling").

C'è anche Hayoo! a proposito.

In Hoogle probabilmente> 95% del tempo non sarà necessario fare questo, ma +Module ad importare un modulo se per qualche motivo non è da ricercare (che è il caso a volte per le biblioteche 3rd party) .
È anche possibile filtrare i moduli di -Module.
Per esempio: destroyTheWorld +World.Destroyer -World.Destroyer.Mercy per trovare destroyTheWorld e assicurarsi che non stai guardando il modo misericordiosa di farlo (questo è molto utile con moduli con gli stessi nomi delle funzioni per le versioni diverse, come quelle in Data.ByteString & Data.ByteString.Lazy, Data.Vector & Data.Vector.Mutable, ecc.).

Oh e un altro vantaggio di Hoogle è che non solo mostra la firma della funzione, ma può anche portarti alle pagine di Haddock del modulo, così ottieni anche la documentazione + in quelle pagine, quando disponibili, puoi fare clic su "Fonte" a destra di ogni funzione per vedere come viene implementata per ulteriori informazioni.

Questo è al di fuori dello scopo della domanda, ma Hoogle è anche usato per interrogare le firme di funzioni che è solo .. incredibilmente utile. Se voglio una funzione che prende un numero di indice e una lista e mi dà l'elemento in quell'indice, e mi chiedo se è già integrato, posso cercarlo in pochi secondi.
So che la funzione accetta un numero e un elenco e mi fornisce un elemento dell'elenco in modo che la firma della funzione debba apparire come segue: Int -> [a] -> a (o genericamente: Num a => a -> [b] -> b), ed entrambe le istanze mostrano che c'è davvero una funzione per quello ((!!) e genericIndex).

Dove GHCi ha il braccio superiore è che è possibile giocare con le espressioni, esplorarle, ecc. Molte volte quando si tratta di funzioni astratte significa molto.
Essere in grado di :l (oad) è davvero molto utile.

Se stai solo cercando firme di funzioni, puoi combinare sia Hoogle che GHCi.
In GHCi è possibile digitare :! cmd e GHCi eseguirà cmd nella riga di comando e stampare i risultati. Ciò significa che puoi anche inserire Hoogle all'interno di GHCi, ad es. :! hoogle void.

Problemi correlati