Reputation: 745
Considering the code below which returns the factorial of a number:
factorial n = go n 1
where
go n ret | n > 1 = go (n-1) (ret * n)
| otherwise = ret
how can I print n
in every single recursive call of go n ret
? Since n
is being decremented each time, I want to see if I can print it every time it gets decremented (like 5 4 3 2 1
).
This is how I tried to do it (which is totally wrong) but I can't think of a different way:
factorial n = go n 1
where
go n ret | n > 1 = go (n-1) (ret * n) print n
| otherwise = ret
Upvotes: 4
Views: 1109
Reputation: 2392
The problem here is you are trying to do some IO (print
) within a pure function (factorial
): you can't do so in Haskell.
One workaround is to use the module Debug.Trace
likewise:
import Debug.Trace
factorial n = go n 1
where
go n ret | ("n value is: " ++ show n) `trace` n > 1 = go (n-1) (ret * n)
| otherwise = ret
And you'll get:
*Main Debug.Trace> factorial 5
n value is: 5
n value is: 4
n value is: 3
n value is: 2
n value is: 1
120
There are some caveats though as stated in the library:
The trace function should only be used for debugging, or for monitoring execution. The function is not referentially transparent: its type indicates that it is a pure function but it has the side effect of outputting the trace message.
As Daniel pointed out, if we want to enforce the multiplication to occur in each step of the evaluation, we have to rewrite this as follows:
factorial n = go n 1
where
go n ret | ("n value is: " ++ show n) `trace` n > 1 = go (n-1) $! (ret * n)
| otherwise = ret
Upvotes: 3
Reputation: 30736
To print, you'll need to lift the value into the I/O Applicative; the type will change from
factorial :: (Num a, Ord a) => a -> a
to
factorial :: (Num a, Ord a, Show a) => a -> IO a
go
action doesThe go
action needs to do two things:
n
go
or produce the resultTo do two things, we need some combinator to combine them. The appropriate one here is *>
:
(*>) :: Applicative f => f a -> f b -> f b
Sequence actions, discarding the value of the first argument.
This is exactly what we need because we don't need to use the value of the first action (the type of the print
action is IO ()
, so it doesn't contain any useful result).
We also need pure
at the end
pure :: Applicative f => a -> f a
Lift a value.
to lift the result into the I/O Applicative.
factorial n = go n 1
where
go n acc =
print n *>
if n > 1
then let !acc' = acc * n
in go (n-1) acc'
else pure acc
The !
in the binding of acc'
forces the multiplication to occur immediately (thanks to Daniel Wagner for pointing out the need for this).
Testing in GHCi:
λ> factorial 5 >>= print
5
4
3
2
1
120
Upvotes: 3
Reputation: 152697
You can use (>>)
to sequence IO
actions and return
to turn pure computations into IO
actions. So:
factorial n = go n 1 where
go n ret | n > 1 = print n >> go (n-1) (ret*n)
| otherwise = return ret
Of course, factorial
is now a function that produces an IO
action; its callers may need to be fixed up to accommodate that.
Also, beware: due to laziness, none of the actual multiplications are done until after all of the printing! This is probably not what you intended. It won't matter much for the kinds of tests you'll be doing on a microbenchmark like this, but for larger functions that do more computation per iteration you might start to notice. You can fix this using $!
or similar to make the computation of the IO
action depend on the computation of this iteration's work. So:
factorial n = go n 1 where
go n ret | n > 1 = print n >> (go (n-1) $! ret*n)
| otherwise = return ret
Upvotes: 1