2015-06-07 18 views
13

I codici sottostanti sembra abbastanza chiaro:Qual è il tipo di variabile in notazione qui in Haskell?

do 
    x <- Just 3 
    y <- Just "!" 
    Just (show x ++ y) 

Qui il tipo di x è Num e y è String. (<- qui viene utilizzato per prendere valore effettivo fuori della Monade)

Tuttavia, questo frammento non sembra così chiaro per me:

import Control.Monad.Instances 
addStuff :: Int -> Int 
addStuff = do 
    a <- (* 2) 
    b <- (+ 10) 
    return (a + b) 

Qual è il tipo di a e del tipo di b qui? Sembra che agiscono come un Num, ma a <- (* 2) e b <- (+ 10) sembra criptico qui ...

Qualcuno ha idee su questo?

+5

'<-' * Non * 'rimuovere' il valore da una monade.È lo zucchero sintattico che * rappresenta * estraendo un valore, ma in realtà lo sta semplicemente manipolando in un modo specifico. Per questo motivo, puoi trattare le funzioni come monadi, perché, mentre non puoi letteralmente "estrarre" il risultato di una funzione, puoi unire le funzioni insieme per crearne un'altra. – AJFarmar

risposta

16

Beh, sei inciampato in una specie di monade strana.

La monade in questione è Monad ((->) r). Ora, cosa significa? Bene, è la monade delle funzioni del modulo r -> *. Ad esempio, di funzioni che accettano lo stesso tipo di input.

Hai chiesto quali sono il tipo di a e b in questa istanza. Bene, sono entrambi Num a => a, ma questo non spiega molto.

Intuitivamente, possiamo capire la monade in questo modo: Un valore monadico è una funzione che assume un valore di tipo r come input. Ogni volta che ci leghiamo alla monade, prendiamo quel valore e lo passiamo alla funzione vincolata.

cioè nel nostro addStuff esempio, se chiamiamo addStuff 5, quindi a è inevitabile che (*2) 5 (che è 10), e b è destinato a (+10) 5 (che è 15).

Vediamo un esempio più semplice da questo monade per cercare di capire come funziona precisamente:

mutate = do a <- (*2) 
      return (a + 5) 

Se desugar ad un vicolo cieco, otteniamo:

mutate = (*2) >>= (\a -> return (a + 5)) 

Ora, questo doesn 't aiutare molto, quindi cerchiamo di usare the definition of bind per questo monade:

mutate = \ r -> (\a -> return (a + 5)) ((*2) r) r 

questo riduce a

mutate = \ r -> return ((r*2) + 5) r 

Che abbiamo utilizzando la definizione che return è const, in grado di ridurre al

mutate = \ r -> (r*2) + 5 

che è una funzione, che moltiplica un numero per 2, e poi aggiunge 5.

Questo è uno monade strana.

+2

Si potrebbe voler nominare il monade (Reader) in modo che il richiedente possa cercare risorse su di esso. :) – Sarah

+0

Non lo chiamerei monade "strano"; è certamente difficile da capovolgere in un primo momento, ma, a mio avviso, è l'esempio perfetto di una Monade come un calcolo irraggiungibile, o contesto computazionale. – AJFarmar

9

Attribuite addStuff

addStuff :: Int -> Int 
addStuff = do 
    a<-(*2) 
    b<-(+10) 
    return (a+b) 

le desugars definizione in

addStuff = 
    (* 2) >>= \a -> 
     (+ 10) >>= \b -> 
      return (a + b) 

passa il mouse sopra il >>= in fpcomplete online editor mostra

:: Monad m => forall a b. 
       (m a  ) -> (a -> m b  ) -> (m b  ) 
::   (Int -> a ) -> (a -> Int -> b ) -> (Int -> b ) 
::   (Int -> Int) -> (Int -> Int -> Int) -> (Int -> Int) 

Questo ci porta a credere che usiamo un'istanza Monade per le funzioni . In effetti, se guardiamo il source code, vediamo

instance Monad ((->) r) where 
    return = const 
    f >>= k = \ r -> k (f r) r 

Utilizzando queste informazioni appena ottenute possiamo valutare la funzione addStuff noi stessi.

Dato l'espressione iniziale

(* 2) >>= (\a -> (+10) >>= \b -> return (a + b)) 

sostituiamo utilizzando la definizione >>=, dando noi (nel seguito {}, [], () solo illustrano diverse profondità di ())

\r1 -> {\a -> (+10) >>= \b -> return (a + b)} {(* 2) r1} r1 

semplificare la seconda -ultimo termine all'interno del lambda più esterno

\r1 -> {\a -> (+10) >>= \b -> return (a + b)} {r1 * 2} r1 

applicano {r1 * 2} a {\a -> ...}

\r1 -> {(+10) >>= \b -> return ((r1 * 2) + b)} r1 

sostituto rimanente >>= con la sua definizione nuovamente

\r1 -> {\r2 -> [\b -> return (r1 * 2 + b)] [(+10) r2] r2} r1 

semplificare secondo a ultimo termine all'interno lambda interna

\r1 -> {\r2 -> [\b -> return (r1 * 2 + b)] [r2 + 10] r2} r1 

applicano [r2 + 10] a 012.

\r1 -> {\r2 -> [return (r1 * 2 + (r2 + 10))] r2} r1 

applica r1 a {\r2 -> ...}

\r1 -> {return (r1 * 2 + r1 + 10) r1} 

sostituto return con la sua definizione

\r1 -> {const (r1 * 2 + r1 + 10) r1} 

valutare const x _ = x

\r1 -> {r1 * 2 + r1 + 10} 

prettify

\x -> 3 * x + 10 

Infine arriviamo

addStuff :: Int -> Int 
addStuff = (+ 10) . (* 3) 
+0

Bella riduzione! Ottimo lavoro, davvero informativo. – AJFarmar