Since your a beginner to Haskell and not doing this simply to learn how continuations and IORefs work, you're doing it wrong.
The Haskell-y way to write an imperative loop is tail-calls or folds.
factorial n = foldl1' (*) [1..n]
factorial' n = go 1 n
where go accum 0 = accum
go accum n = go (n-1) (accum * n)
Also since Haskell's callCC
in essence provides you an early return, using it to simulate loops is not going to work.
callCC (\c -> ???)
Think about what we would have to put in for ???
in order to loop. somehow, we want to run callCC
again if it returns a certain value, otherwise just keep going on our merry way.
But nothing we put in ???
can make the callCC
run again! It's going to return a value no matter what we do. So instead we'll need to do something around that callCC
let (continue, val) = callCC (someFunc val)
in if continue
then callCallCCAgain val
else val
Something like this right? But wait, callCallCCAgain
is recursion! It's even tail recursion! In fact, that callCC
is doing no one any good
loop val = let (continue, val') = doBody val
in if continue
then loop val'
else val'
Look familiar? This is the same structure as factorial'
above.
You can still use IORef
s and something like the monad-loops package, but it's going to be an uphill battle always because Haskell isn't meant to be written like that.
Summary
When you want to directly do "loops" in haskell, use tail recursion. But really, try to use combinators like fold
and map
, they're like little specialized loops and GHC is fantastic at optimizing them. And definitely don't use IORef
s, trying to program Haskell like it's C is just going to hurt your performance, readability, and everyone will be sad.