Haskell newbie here.
I have a higher order function myTransform
at hand, which takes a function fn :: String -> String
and do some fancy things.
Let's assume the implementation is
myTransform :: Int -> (String -> String) -> String -> [String]
myTransform n f = take n . iterate f
now I want to transform an external program, which is, if I understand right, an IO action. Preferably, the signature should be String -> IO String
:
import System.Process
externProg :: String -> IO String
externProg s = readProcess "echo" ["-n", s, "+ 1"] ""
the question is, is there any way I can fit this String -> IO String
function into that String -> String
argument slot, without changing, or even knowing, how myTransform
implements?
No, you can not. You will have to make a monadic version of myTransform. It is custom to append a capital M. I.e. map becomes mapM. fold becomse foldM ... Unfortunately there is no iterateM. I would therefore skip iterateM
and implement it directly.
myTransformM' :: (Monad m) => Int -> (String -> m String) -> String -> m [String]
myTransformM' 0 f str = return [str]
myTransformM' n f str = do
results <- myTransformM (n-1) f str
next <- f (head results)
return (next:results)
myTransformM n f str = do
results <- myTransformM' n f str
return $ reverse results
You might notice that the results of the first function are ordered the other way around. This is in order to avoid the function being quadratic.
You can try yourself what will happen if you implement iterateM
. It will just loop eternally. This is because Haskell can never know if you will actually get a list back or if there will be an IOError somewhere down the road. Similarly if you take the Maybe monad, Haskell will never know if you actually get a Just list
back or if down the road somewhere there is a Nothing
.
iterateM :: (Monad m) => (a -> m a) -> a -> m [a]
iterateM f a = do
result <- f a
results <- iterateM f result
return (result:results)