Hi everyone! I'm a beginner at Haskell and this is my first question. I figured out how to make things work myself and asking this just to understand Haskell better and improve my code.
I want to write a file, but its path might exist already so I want to find a free path by adding numbers to the path. E.g. if I already have "foo.txt"
then I want to write to "foo 1.txt"
, then "foo 2.txt" and so on.
So I wrote this function:
findFreeName :: FilePath > IO FilePath
findFreeName path = head <$> filterM isPathFree (map makePathWithNumber [1, 2 ..])
where
 skipping implementation here:
isPathFree :: FilePath > IO Bool
 uses the path parameter and turns x to "foo x.txt" unless x is 0
makePathNumber :: Int > FilePath
And it started to check paths "foo.txt"
, "foo 1.txt"
, "foo 2.txt"
and so on forever. I guess I understand that this happens because filterM
has type (a > m Bool) > [a] > m [a]
which means that once you use its result, it has to finish the action first and only then you can operate on its result. It's an equivalent of writing:
findFreeName path = do
 first get a value from an IO action that we can't do partially:
freePaths < filterM isPathFree (map makePathWithNumber [1, 2 ..])
 only then do other actions / computations:
return head freePaths
This is how I fixed the function so that it does a finite number of IO actions:
findFreeName path = takeFirstFreePath 0
where
takeFirstFreePath i = do
let p = makePathWithNumber i
isFree < isPathFree p
if isFree then return p else takeFirstFreePath (i + 1)
It's a recursive function which is more difficult to read than a simple filter
, so I tried to write a version of filterM
that suites my needs:
filterMBetter :: Monad m => (a > m Bool) > [a] > [m a]
filterMBetter _ [] = []
filterMBetter pred (x:xs) = do
isTrue < pred x
if isTrue
then (return x) : filterMBetter pred xs
else filterMBetter pred xs
and it fails to compile with the error:
• Couldn't match type ‘m’ with ‘[]’
‘m’ is a rigid type variable bound by
the type signature for:
filterMBetter :: forall (m :: * > *) a.
Monad m =>
(a > m Bool) > [a] > [m a]
at app/saveWords/SaveWords.hs:77:157
Expected type: [Bool]
Actual type: m Bool
• In a stmt of a 'do' block: isTrue < pred x
In the expression:
do isTrue < pred x
if isTrue then
(return x) : filterMBetter pred xs
else
filterMBetter pred xs
In an equation for ‘filterMBetter’:
filterMBetter pred (x : xs)
= do isTrue < pred x
if isTrue then
(return x) : filterMBetter pred xs
else
filterMBetter pred xs
• Relevant bindings include
pred :: a > m Bool (bound at app/saveWords/SaveWords.hs:79:15)
filterMBetter :: (a > m Bool) > [a] > [m a]
(bound at app/saveWords/SaveWords.hs:78:1)

80  isTrue < pred x
 ^^^^^^
which I interpret as "[]
is a monad and my function returns [m a]
so the do
block is in the context of []
monad, not the m
one, so I could do x < [y]
, but not x < pred y
"
Now I have a couple of questions:
1. Is there a way to make my filterMBetter
function working? If not, how to formulate the reason for this in simple words?
2. I guess I went in a wrong direction when I decided to implement my own function filterMBetter :: Monad m => (a > m Bool) > [a] > [m a]
instead of just writing a recursive IO action. What would be a better approach in this situation?
3. Is there a way to do what I want using only standard library function or at most some package from Stackage, without inventing my own wheel? If yes, please share an example.
Want to add to the discussion?
Post a comment!