2010-10-30 5 views
6

Si prega di sopportare me come sono molto nuovo alla programmazione funzionale e Haskell. Sto tentando di scrivere una funzione in Haskell che prende una lista di numeri interi, stampa la testa di detta lista e poi restituisce la coda della lista. La funzione deve essere di tipo [Intero] -> [Intero]. Per dare un po 'di contesto, sto scrivendo un interprete e questa funzione viene chiamata quando il suo rispettivo comando viene cercato in una lista associativa (la chiave è il comando, il valore è la funzione).Haskell: I/O e ritorno da una funzione

Ecco il codice che ho scritto:

dot (x:xs) = do print x 
     return xs 

Il compilatore fornisce il seguente messaggio di errore:

forth.hs:12:1: 
Couldn't match expected type `[a]' against inferred type `IO [a]' 
    Expected type: ([Char], [a] -> [a]) 
    Inferred type: ([Char], [a] -> IO [a]) 
In the expression: (".", dot) 

Ho il sospetto che la chiamata a stampare nella funzione di punti è ciò che sta causando la il tipo dedotto deve essere IO [a]. C'è un modo in cui posso ignorare il tipo di stampa di ritorno, in quanto tutto ciò che devo restituire è la coda della lista passata in punto.

Grazie in anticipo.

risposta

8

Nella maggior parte dei linguaggi funzionali, ciò funzionerebbe. Tuttavia, Haskell è una lingua funzionale pura. Non v'è permesso di fare IO funzioni, per cui la funzione può essere

  1. [Int] -> [Int] senza eseguire alcuna IO o
  2. [Int] -> IO [Int] con IO

Il tipo di dot dedotta dal compilatore è dot :: (Show t) => [t] -> IO [t] ma è possibile dichiarare che sia [Int] -> IO [Int]:

dot :: [Int] -> IO [Int]

vedere Io monade: http://book.realworldhaskell.org/read/io.html


che non ho menzionato System.IO.Unsafe.unsafePerformIO che deve essere utilizzato con grande cura e con una solida conoscenza delle sue conseguenze.

+3

Il [ 'Debug.Trace'] (http: // www Il modulo .haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Debug-Trace.html) può essere utilizzato anche per stampare le cose. –

8

No, la funzione causa effetti indesiderati (ovvero IO, in questo caso la stampa sullo schermo) o non lo è. print fa IO e quindi restituisce qualcosa in IO e questo non può essere annullato.

E sarebbe una brutta cosa se il compilatore potesse essere indotto a dimenticare lo IO. Ad esempio se la tua funzione [Integer] -> [Integer] viene chiamata più volte nel tuo programma con gli stessi parametri (ad esempio []), il compilatore potrebbe perfettamente eseguire la funzione solo una volta e utilizzare il risultato di quella in tutti i punti in cui la funzione ha ottenuto " chiamato". La tua stampa "nascosta" verrebbe eseguita solo una volta, anche se hai chiamato la funzione in più punti.

Ma il tipo di sistema ti protegge e assicura che tutte le funzioni che usano IO, anche se solo indirettamente, hanno un tipo IO per riflettere questo. Se si desidera una funzione pura non è possibile utilizzare print in esso.

5

Come forse già sapete, Haskell è un linguaggio di programmazione funzionale "puro".Per questo motivo, gli effetti collaterali (come la stampa di un valore sullo schermo) non sono casuali in quanto sono presenti in più lingue tradizionali. Questo fatto dà ad Haskell molte belle proprietà, ma ti verrebbe perdonato di non preoccuparti di questo quando tutto ciò che stai facendo è provare a stampare un valore sullo schermo.

Poiché la lingua non ha una funzione diretta per causare effetti collaterali, la strategia è che le funzioni possono produrre uno o più valori di "azione IO". Un'azione IO incapsula un qualche effetto collaterale (stampa sulla console, scrittura su un file, ecc.) E probabilmente produce un valore. La tua funzione dot sta producendo proprio tale azione. Il problema che ora hai è che hai bisogno di qualcosa che sia in grado di causare l'effetto collaterale di IO, oltre a scartare il valore ed eventualmente trasferirlo nel tuo programma.

Senza ricorrere agli hack, questo significa che è necessario riportare le azioni IO alla funzione main. In pratica, questo significa che tutto tra main e dot deve essere nella "Monade IO". Ciò che accade nella "IO Monade" rimane, per così dire, nella "Monade IO".

EDIT

Ecco riguardo l'esempio più semplice che potessi immaginare per utilizzare la funzione dot in un programma Haskell valida:

module Main where 

main :: IO() 
main = 
    do 
     let xs = [2,3,4] 
     xr <- dot xs 
     xrr <- dot xr 
     return() 

dot (x:xs) = 
    do 
     print x 
     return xs