2011-10-20 1 views
7

Sto definendo un esempio di una monade come segue:Haskell sa sempre quale "ritorno" chiamare?

data Something = Something a 

instance Monad Something where 
    return a = Something a  --Wraps a in 'Something', correct? 
    m >>= f = do 
     var <- m 
     return $ f var   --I want this to pass var to f, then wrap the result 
            --back up in the 'Something' monad with the return I 
            --Just defined 

Le domande sono ->

1: Ci sono evidenti errori/malintesi con quello che sto facendo?

2: Will Haskell sapere per chiamare il ritorno che ho sopra definito dal m >>= f

3: Se per qualche motivo definisco un'altra funzione

f :: Something a -> Something b 
f x = do 
    var <- x 
    return $ doMagicTo x 

Will return chiamata al ritorno ho definito in monade istanza e avvolgere x in Something?

risposta

15

Ci sono alcuni grossi problemi qui.

In primo luogo, un'istanza Monad deve avere kind* -> *. Ciò significa che è necessario almeno un tipo variabile, dove il tuo Something non ne ha. Per fare un confronto:

-- kind * -> * 
Maybe 
IO 
Either String 

-- kind * 
Maybe Int 
IO() 
Either String Double 

mostra come ogni Maybe, IO, e Either String hanno bisogno di un parametro di tipo prima di poterli usare? Con Something, non c'è posto per il parametro tipo di compilare Quindi è necessario modificare la definizione:.

data Something a = Something a 

Il secondo grande problema è che il >>= nell'istanza Monade è sbagliato. Generalmente non è possibile utilizzare la notazione perché chiama semplicemente le funzionie >>=. Quindi è necessario scriverlo senza alcuna funzione di monad, con notazione o chiamata >>= o return.

instance Monad Something where 
    return a = Something a  --Wraps a in 'Something' 
    (Something m) >>= f = f m  --unwraps m and applies it to f 

La definizione di >>= è più semplice del previsto. Unwrapping m è semplice perché è sufficiente associare il modello al costruttore Something. Inoltre, f :: a -> m b, quindi non devi preoccuparti di riavvolgerlo nuovamente, perché lo fa f.

Mentre non c'è alcun modo per scartare una monade in generale, moltissimi specifici monadi si può scartare.

Tenere presente che non c'è nulla di sintatticamente sbagliato nell'usare la notazione o >>= nella dichiarazione di istanza monad. Il problema è che >>= è definito in modo ricorsivo in modo che il programma entri in un ciclo infinito quando si tenta di utilizzarlo.

(N.B. Something come definito qui è il Identity monad)

Per la terza domanda, sì, la funzione return definito nell'istanza Monade è quella che verrà chiamata. Le classi di tipi vengono inviate per tipo e, poiché il tipo specificato deve essere Something b, il compilatore utilizzerà automaticamente l'istanza Monad per Something. (Penso che intendessi l'ultima riga da doMagicTo var).

+3

Questo è fantastico. Tutte queste risposte sono così buone. Mi sento in dovere di sottolineare che la comunità di Haskell finora è stata la più utile e completa di tutti quelli che ho incontrato. Ciao ragazzi. – providence

2
  1. Chiudi. Lo standard return è ridondante qui ed è necessario aggiungere un parametro di tipo al costruttore del tipo Something. Modifica: questo codice è ancora errato. La definizione di >>= è circolare. Vedi le altre risposte per maggiori informazioni.

    data Something a = Something a 
    
    instance Monad Something where 
        return a = Something a 
        m >>= f = do 
         var <- m 
         f var 
    
  2. Dal momento che la tua definizione di >>= è sotto il instance Monad Something where, tipo >>= s' è Something a -> (a -> Something b) -> Something b. Quindi può dire che f var deve essere di tipo Something b. Il termine per google qui è "tipo di inferenza"

  3. Sì. Di nuovo, questo è inferenza di tipo. Se il compilatore non può dedurre il tipo che vuoi, te lo dirà. Ma di solito è possibile.

+0

vedere Giovanni L's soluzione: non è possibile utilizzare un fai-blocco per definire '>> =' così, perché come pensi che il fai-blocco viene de-zuccherato? (Anche indicato nella soluzione di rampion.) – ivanm

+0

D'oh, buona chiamata. Devo pensare più come un programmatore Haskell ora, dal momento che ho appena preso il codice dell'OP, l'ho modificato fino a quando non ho controllato e compilato, e ho detto, "Sì, deve essere giusto." : P Comunque, penso che la domanda dell'OP fosse più o meno "Haskell fa tipo di inferenza?" anche se non l'hanno chiesto in questo modo. E il mio punto principale era, sì, lo fa. – MatrixFrog

7

Il problema principale è che la definizione di >>= è circolare.

Haskell facciamolo sintassi è lo zucchero syntatic per catene di >>= e >>, così la tua definizione

m >>= f = do 
    var <- m 
    return $ f var   

Desugars a

m >>= f = 
    m >>= \var -> 
    return $ f var 

così si sta definendo come m >>= fm >>= \..., che è circolare.

Quello che devi fare con >>= è definire come estrarre un valore da m per passare a f. Inoltre, il tuo f dovrebbe restituire un valore monadico, quindi l'uso di return non è corretto qui (questo è più vicino al modo in cui definiresti fmap).

Una definizione di >>= per Something potrebbe essere:

(Something a) >>= f = f a 

Questa è la Monade Identity - c'è un bel po 'scritta su di esso - è un buon punto di partenza per capire come il lavoro monadi.

Problemi correlati