Haskell: I/O and Returning From a Function
Please bear with me as I am very new to functional programming and Haskell. I am attempting to write a function in Haskell that takes a list of Integers, prints the head of said list, and then returns the tail of the list. The function needs to be of type [Integer] -> [Integer]. To give a bit of context, I am writing an interpreter and this function is called when its respective command is looked up in an associative list (key is the command, value is the function).
Here is the code I have written:
dot (x:xs) = do print x
return xs
The compiler gives the following error message:
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)
I suspect that the call to print in the dot function is what is causing 开发者_运维问答the inferred type to be IO [a]. Is there any way that I can ignore the return type of print, as all I need to return is the tail of the list being passed into dot.
Thanks in advance.
In most functional languages, this would work. However, Haskell is a pure functional language. You are not allowed to do IO in functions, so the function can either be
[Int] -> [Int]
without performing any IO or[Int] -> IO [Int]
with IO
The type of dot
as inferred by the compiler is dot :: (Show t) => [t] -> IO [t]
but you can declare it to be [Int] -> IO [Int]
:
dot :: [Int] -> IO [Int]
See IO monad: http://book.realworldhaskell.org/read/io.html
I haven't mentioned System.IO.Unsafe.unsafePerformIO
that should be used with great care and with a firm understanding of its consequences.
No, either your function causes side effects (aka IO, in this case printing on the screen), or it doesn't. print
does IO and therefore returns something in IO
and this can not be undone.
And it would be a bad thing if the compiler could be tricked into forgetting about the IO
. For example if your [Integer] -> [Integer]
function is called several times in your program with the same parameters (like []
for example), the compiler might perfectly well just execute the function only once and use the result of that in all the places where the function got "called". Your "hidden" print would only be executed once even though you called the function in several places.
But the type system protects you and makes sure that all function that use IO
, even if only indirectly, have an IO
type to reflect this. If you want a pure function you cannot use print
in it.
As you may already know, Haskell is a "pure" functional programming language. For this reason, side-effects (such as printing a value on the screen) are not incidental as they are in more mainstream languages. This fact gives Haskell many nice properties, but you would be forgiven for not caring about this when all you're doing is trying to print a value to the screen.
Because the language has no direct facility for causing side-effects, the strategy is that functions may produce one or more "IO action" values. An IO action encapsulates some side effect (printing to the console, writing to a file, etc.) along with possibly producing a value. Your dot
function is producing just such an action. The problem you now have is that you need something that will be able to cause the IO side-effect, as well as unwrapping the value and possibly passing it back into your program.
Without resorting to hacks, this means that you need to get your IO action(s) back up to the main
function. Practically speaking, this means that everything between main
and dot
has to be in the "IO Monad". What happens in the "IO Monad" stays in the "IO Monad" so to speak.
EDIT
Here's about the simplest example I could imagine for using your dot
function in a valid Haskell program:
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
精彩评论