Explicit looping is commonplace in imperative programming languages. For example, this is how I might write a loop in Python.
= 0 wrongAttempts while True: print("Enter password") = input() s if s != "letmein": += 1 wrongAttempts print("You have been incorrect", wrongAttempts, "times") else: break print("You are into the system!")
We can always replace imperative looping with recursion.
= do main let myLoop wrongAttempts = do putStrLn "Enter password" <- getLine s /= "letmein") $ do when (s let wrongAttempts' = wrongAttempts + 1 putStrLn "You have been incorrect " ( ++ show wrongAttempts' ++ " times" ) myLoop wrongAttempts' 0 myLoop putStrLn "You are into the system!"
There are a couple of small differences between the Python version and the Haskell version, and two bigger ones. The small ones are that in the Haskell version we explicitly continue by naming
myLoop whereas the Python version explicitly
breaks instead, and naturally the Haskell version explicitly passes the state for the next iteration.
The two bigger differences make the Haskell version clumsier. Firstly we have to name
myLoop and call it explicitly. Secondly the initial state (
0) is passed in far from the definition of
How can we fix this? Let’s define a genereric looping combinator,
loop. To make my loop less clumsy I want to avoid naming
myLoop and I want to pass the initial state in to
loop directly. This suggests I want to write something like
0 (\continue wrongAttempts -> do loop ... continue wrongAttempts')
where the loop name
continue is now bound in a lambda.
Can we implement this? Well,
loop will have to look like
= body ... stateloop state body
but what can I pass as the second argument to
body? It needs to be something that takes the updated state and continues with the loop. But that’s just
flip loop body! So I can define
= body (flip loop body) stateloop state body
and then I can write
main2 which doesn’t have the original clumsinesses.
= do main2 loop0 -> do (\continue wrongAttempts putStrLn "Enter password" <- getLine s /= "letmein") $ do when (s let wrongAttempts' = wrongAttempts + 1 putStrLn "You have been incorrect " ( ++ show wrongAttempts' ++ " times" ) continue wrongAttempts' )putStrLn "You are into the system!"
flip loop body state = body (flip loop body) state so
flip loop body = body (flip loop body). If we write
fix f = f (fix f) then
loop = flip fix. N.B.
fix is normally written as
fix f = let x = f x in x for efficiency.