开发者

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

  1. [Int] -> [Int] without performing any IO or
  2. [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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜