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
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With