Iterative in-place Haskell factorial
Iterative in-place Haskell factorial
This is the implementation of factorial which does not use recursion and does not pass result via stack but use 2 "global" vars: counter abd factorial value. For example:
fact 5 = 120
counter | fact
---------+-----------
5 | 5
4 | 20
3 | 60
2 | 120
1 | 120 => end
Instead of pass temporary result of multiplication via stack, we'll
create 2 states and use them as counter and factorial value. How to
create 2 states? For one state we can use State monad, for 2…
state in the state. Adding state to another monad (possible state too)
is responsibility of StateT transformer. Calculation will looks like
StateT Integer (State Integer) (). This means that we use Integer
for counter and multiplication result, nothing will be returned as
result because we use as result the state.
import Control.Monad
import Control.Monad.Trans.State
import Control.Monad.Trans.Class
-- | StateT s m a :: * -> (* -> *) -> * -> *
-- here "external" is in signature, not actually - in call
fact1 :: StateT Integer (State Integer) ()
fact1 = do
i <- get -- ^ counter: external state
when (i > 1) $ do -- ^ if counter > 1
lift $ modify (i*) -- ^ mult internal state by counter (initial: 1)
modify (-1+) -- ^ decrement external state
fact1 -- ^ to next iteration, changed states
fact :: Integer -> Integer
fact n = execState (execStateT fact1 n) 1
-- ^ external (internal in signature) state is fact, internal is counter
main :: IO ()
main = do
putStrLn "Result: "
print $ fact 5
Implementation is simple. First, signature describes only types, not
actual nesting structure, so in function body "internal" is the internal
in signature. lift~/~get are relative for internal/external but in
termins of signature. Really after transformation you will works with
stack-like structure, so when you call your function, you should create
all in reverse order: internal becomes external, so we get counter as
external state but pass it in call as internal. No collision here: it's
actually internal, only in signature it's looks like external, because
signature looks like application of StateT (externally) to argument
State.
We nothing return because there is a way to return state instead of result.
This does exec* functions family (execState is needed in external call).