The type
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
What the go
action does
The go
action needs to do two things:
- print the number
n
- either recursively call
go
or produce the result
How to do two I/O actions sequentially
To 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).
Lifting a value into I/O
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.
The code
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