Is there any way to print a variable inside a function in Haskell?

Viewed 737

I am a beginner in Haskell who has a background in imperative languages, and I wanted to know if it was possible to print in a Haskell function and not print in the main.

merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys)
    | x < y = x:(merge xs (y:ys))
    | otherwise = y:(merge (x:xs) ys)

I wanted to see the list's contents after every merge operation, but an error occurs when I tried inserting a print statement in this function. This code is part of a program that does a merge sort operation, so I cannot place a print statement in the main function. I would use the ghci, and compile the merge function from a separate file to manually see how the merge function works, but I became curious if it was possible to see the list's contents in the merge function itself.

How would I show the contents of the list given this merge function? Do I need to recode the whole merge function in another way to see the contents of the list? If so, how would it look like?

1 Answers

In general: no, you cannot print from just any function in Haskell. This is because normal Haskell functions cannot have any side-effects, of which printing is an example. (Other side-effects: mutating a variable, playing sound, sending a HTTP request.) Any side-effect in Haskell must be wrapped in an IO type, so if you want to use print in this function, you must wrap the whole function in IO:

merge1 :: (Ord a, Show a) => [a] -> [a] -> IO [a]
merge1 [] ys = return ys
merge1 xs [] = return xs
merge1 (x:xs) (y:ys)
    | x < y =
        let merged = x:(merge1 x (y:ys)) in do
            print merged
            return merged
    | otherwise =
        let merged = y:(merge1 (x:xs) ys) in do
            print merged
            return merged

As you can see, this is horribly verbose. It also means that merge1 must be wrapped in IO, so any other function which uses it must also be in IO. (This is in fact often seen as an advantage, since it allows you to deduce the behaviour of a function from its type: it means that any function not in IO cannot have side effects, and vice versa.)

However, in your particular case, there is an escape route. The Debug.Trace module contains a whole bunch of functions for printing to the console without IO, to be used for debugging purposes only. The basic function is trace s x, which will print the string s to the console and return x. In your case, I suggest using traceShowId x, which will print x, then return it:

merge2 :: (Ord a, Show a) => [a] -> [a] -> [a]
merge2 [] ys = ys
merge2 xs [] = xs
merge2 (x:xs) (y:ys)
    | x < y = traceShowId $ x:(merge2 x (y:ys))
    | otherwise = traceShowId $ y:(merge2 (x:xs) ys)
Related