Haskell - binding a mapped monadic function - haskell

Within this block of code here, I have a function tagsort that takes a FilePath and returns an IO String.
builddir xs = do
writeto <- lastest getArgs
let folderl b = searchable <$> (getPermissions b)
let filel c = ((lastlookup mlookup c) &&) <$> ((not <$> folderl c))
a <- listDirectory xs
listdirs <- filterM (folderl) (map ((xs ++ "/") ++) a)
filedirs <- filterM (filel) (map ((xs ++ "/") ++) a)
tagfiles <- tagsort <$> filedirs
--testprint to terminal
putStrLn $ concat listdirs
putStrLn $ concat tagfiles
tagsort :: Control.Monad.IO.Class.MonadIO m => FilePath -> m [Char]
tagsort xs = do
nsartist <- getTags xs artistGetter
nsalbum <- getTags xs albumGetter
let artist = init $ drop 8 $ show nsartist
let album = init $ drop 7 $ show nsalbum
pure (artist ++ " - " ++ album)
I'd like to take this function and map it over a list of directories. When run, I get this error.
• Couldn't match type ‘[]’ with ‘IO’
Expected type: IO (t0 [Char])
Actual type: [t0 [Char]]
• In a stmt of a 'do' block: tagfiles <- tagsort <$> filedirs
I believe I understand what is happening here. In order to bind in the way I desire to tagfiles, I would want an IO [String], but instead mapping tagsort to the list filedirs produces [IO String]. I am not totally sure how to circumvent this or if it is even able to be circumvented at all. Perhaps mapping is not the correct way to do this? Any help would be appreciated.

This is because the function tagsort is of type String -> IO String
Note: I am using IO for simplicity and String for both [Char] and FilePath.
However, when mapping it onto filedir :: [String] using
(<$>) = fmap :: Functor f => (a -> b) -> f a -> f b
a conflict occured between IO [String] and `[IO String] - the former is what the compiler expects an expression in a do block to be.
This isn't very useful on first glance. However, haskell has a function called sequence for this exact task. Its constraints needn't matter much now, as Foldable is something completely different. For now, know that it can be of type [IO a] -> IO [a]
Luckily again, there's a very useful predefined utility function, mapM just for sequence . map f.
The final code will be:
mapM tagSort fileDirs

Related

Haskell: Wrong typing when making an IO Map out of IO Strings

I have a function with the following signature:
Config -> IO (Map.Map String String)
that is used to extract a FilePath from the Config, then load that file (contains lines of text), and then parse the lines into keys and values - which is why I need to save them into map, preferably.
So, I have created the following function:
let lns = liftM lines $ readFile $ getSet $ listF c in
foldM (\m n -> liftM3 Map.insert n n m) (return Map.empty) lns
Now, I would hope that Haskell realizes that I want this to be a IO Map of Strings (for simplicity, I dont parse the lines into keys and vals for now, and just put the whole line in there).
However, I get the following errors:
• Couldn't match type ‘Map.Map String’ with ‘IO’
Expected type: IO (Map.Map String String)
Actual type: Map.Map
String (Map.Map String (Map.Map String String))
• In the expression:
foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns
In the expression:
let lns = liftM lines $ readFile $ getSet $ listF c
in foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns
In an equation for ‘getEpisodeNames’:
getEpisodeNames c
| listF c == NotSet = return Map.empty
| otherwise
= let lns = liftM lines $ readFile $ getSet $ listF c
in foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns
• Couldn't match type ‘[Char]’ with ‘Map.Map String String’
Expected type: Map.Map
String (Map.Map String (Map.Map String String))
Actual type: Map.Map String (Map.Map String String)
• In the expression: liftM3 Map.insert n n m
In the first argument of ‘foldM’, namely
‘(\ m n -> liftM3 Map.insert n n m)’
In the expression:
foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns
• Couldn't match type ‘[]’ with ‘Map.Map String’
Expected type: IO (Map.Map String String)
Actual type: IO [String]
• In the third argument of ‘foldM’, namely ‘lns’
In the expression:
foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns
In the expression:
let lns = liftM lines $ readFile $ getSet $ listF c
in foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns
From that, it seems that return Map.empty tries to wrap the Map in something else than IO monad, which honestly does not make sense to me.
As Haskell never makes mistakes, it is clear to me that I have messed up somewhere, I just can't find, where (though I am pretty sure it is in the second line, as I have checked the lns for the correct type using :t in ghci). I also tried rewriting the function to >>= notation instead of let ... in ..., but it didn't help. Thanks for any help.
The signature of foldM is
foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
Let's see what you've got:
return Map.empty :: IO (Map String String)
lns :: IO [String]
That definitely does not match parameters a and [b]. Fixing the former is easy: just use Map.empty. For the latter, you need access to what's inside lns, which is what the bind operator is for:
>>= :: m a -> (a -> m b) -> m b
Hence
lns >>= foldM (…) Map.empty
But since you operation is pure, you might as well just use fmap and foldl
foldl (…) Map.empty <$> lns
Ok, so I have actually managed to fix the code, though I am still not entirely sure where the problem was, so i would be still grateful if someone could point that out.
So, instead of
let lns = liftM lines $ readFile $ getSet $ listF c in
foldM (\m n -> liftM3 Map.insert n n m) (return Map.empty) lns
I used
(readFile . getSet $ listF c)
>>= (\tx -> return $ lines tx)
>>= (\lns -> return $ foldl (\m n -> Map.insert n n m) Map.empty lns)

Is there a more readable way of rewriting this pure function to use the Writer Monad?

I wrote a recursive algorithm using a list comprehension to perform the recursion. I thought my code was clear and easy to read, and the results it produced were correct.
However, I found it difficult to understand the performance of my code on certain inputs. I thought it would be useful to use the Writer monad to put some logging into my code.
I found it quite difficult to convert my non-monadic code to monadic. Eventually I got it to compile and run correctly, but the monadic code is much less understandable than the original.
The original problem is too complex to explain here, so I have written a toy example that shows the non-monadic and monadic approaches, but doesn't actually calculate anything useful!
So my question is: is there a better way to write the function fMonadic, such that it is more readable?
import Control.Monad (forM)
import Control.Monad.Writer (Writer, runWriter, tell)
fun :: Int -> [[Int]] -> [[Int]]
fun a b = map (map (a +)) b
fNonMonadic :: [[Int]] -> [[Int]]
fNonMonadic [] = [[]]
fNonMonadic (first : rest) =
[ first ++ s
| e <- first
, s <- fNonMonadic $ fun e rest]
fMonadic :: [[Int]] -> Writer [String] [[Int]]
fMonadic [] = do
tell ["base case"]
return [[]]
fMonadic (first : rest) =
fmap concat . forM first $ \ e -> do
tell ["recursive case " ++ show e]
fmap (map (first ++)) $ fMonadic $ fun e rest
main = do
let arg = [[0, 1], [20, 30], [400, 500]]
print $ fNonMonadic arg
let (a, b) = runWriter $ fMonadic arg
print a
mapM_ putStrLn b
It is often awkward to equip pure Haskell functions, which are structured in an algebraic, highly branched tree manner, with a monadic feature such as logging, which requires a more “imperative” structure. However, sometimes it is actually quite natural to write even pure computations using monadic combinators, and yours is in fact one of these. Namely, the list comprehension that's at the heart of fNonMonadic is already basically using the list monad; it can be written thus:
type ListM = [] -- Just to distinguish where I use list as a monad
fNonMonadic :: [[Int]] -> ListM [Int]
fNonMonadic [] = return []
fNonMonadic (first : rest) = do
e <- first
s <- fNonMonadic $ fun e rest
return $ first ++ s
Starting from that, it is much easier to add the writer functionality, by using it as the base of a monad transformer stack. The list must then be used in transformer shape, too:
import Control.Monad.Trans.List
fMonTrafo :: [[Int]] -> ListT (Writer [String]) [Int]
fMonTrafo [] = do
lift $ tell ["base case"]
return []
fMonTrafo (first : rest) = do
e <- ListT $ pure first
lift $ tell ["recursive case " ++ show e]
s <- fMonTrafo $ fun e rest
return $ first ++ s
You may notice that the documentation of ListT warns that the base monad should be commutative, which Writer actually isn't – the order of log entries may get messed up. I don't know if this matters here. If yes, check out the alternative implementation suggested by Daniel Wagner.
I looked at several alternatives to Control.Monad.Trans.List, and soon settled on module ListT from Volkov's list-t package.
This gives the same result as my ugly fMonadic function, but more readable code. It also works correctly, and leads to readable code, in the real problem I want to solve.
In the real problem, the ListT-based code runs slightly more slowly than the ugly code, but the difference isn't enough to matter.
Many thanks again for leftaroundabout's help with this.
For reference, here is the revised version of the toy example, doing the computation the three different ways and showing that the answer is the same:
import Control.Monad (forM)
import ListT (ListT, fromFoldable, toList)
import Control.Monad.Writer (Writer, lift, runWriter, tell)
fun :: Int -> [[Int]] -> [[Int]]
fun a b = map (map (a +)) b
fNonMonadic :: [[Int]] -> [[Int]]
fNonMonadic [] = [[]]
fNonMonadic (first : rest) = do
e <- first
s <- fNonMonadic $ fun e rest
return $ first ++ s
-- The above do notation means the same as this list comprehension:
-- [ first ++ s
-- | e <- first
-- , s <- fNonMonadic $ fun e rest]
fMonadic :: [[Int]] -> Writer [String] [[Int]]
fMonadic [] = do
tell ["base case"]
return [[]]
fMonadic (first : rest) =
fmap concat . forM first $ \ e -> do
tell ["recursive case " ++ show e]
fmap (map (first ++)) $ fMonadic $ fun e rest
fMonTrafo :: [[Int]] -> ListT (Writer [String]) [Int]
fMonTrafo [] = do
lift $ tell ["base case"]
return []
fMonTrafo (first : rest) = do
e <- fromFoldable first
lift $ tell ["recursive case " ++ show e]
s <- fMonTrafo $ fun e rest
return $ first ++ s
main = do
let arg = [[0, 1], [20, 30], [400, 500]]
let x = fNonMonadic arg
print x
let (a, b) = runWriter $ fMonadic arg
print a
mapM_ putStrLn b
let (c, d) = runWriter $ toList $ fMonTrafo arg
print c
mapM_ putStrLn d
putStrLn $ if x == a then "fNonMonadic == fMonadic" else error ""
putStrLn $ if x == c then "fNonMonadic == fMonTrafo" else error ""
putStrLn $ if b == d then "fMonadic log == fMonTrafo log" else error ""

How to combine Arbitrary and IO monads?

I am trying to write a program which writes to a file a list of data generated by an Arbitrary instance, and I am having trouble combining the Arbitrary and IO monads.
A simplified version of what I am trying to do is shown below.
main = do
let n = 10
list <- vector n
writeFile "output.txt" (unlines $ show <$> list)
This leads to a type error since writeFile's IO monad does not match vector's Gen monad.
TestCases.hs:31:3: error:
• Couldn't match type ‘IO’ with ‘Test.QuickCheck.Gen.Gen’
Expected type: Test.QuickCheck.Gen.Gen ()
Actual type: IO ()
• In a stmt of a 'do' block:
writeFile "output.txt" (unlines $ show <$> list)
In the expression:
do { let n = 10;
list <- vector n;
writeFile "output.txt" (unlines $ show <$> list) }
In an equation for ‘main’:
main
= do { let n = ...;
list <- vector n;
writeFile "output.txt" (unlines $ show <$> list) }
I have tried using liftIO to resolve this type mismatch, but it appears that this does not work due to Gen lacking a MonadIO instance.
main = do
let n = 10
list <- vector n :: Gen [Integer]
liftIO $ writeFile "output.txt" (unlines $ show <$> list)
gives the error
TestCases.hs:32:3: error:
• No instance for (MonadIO Gen) arising from a use of ‘liftIO’
• In a stmt of a 'do' block:
liftIO $ writeFile "output.txt" (unlines $ show <$> list)
In the expression:
do { let n = 10;
list <- vector n :: Gen [Integer];
liftIO $ writeFile "output.txt" (unlines $ show <$> list) }
In an equation for ‘main’:
main
= do { let n = ...;
list <- vector n :: Gen [Integer];
liftIO $ writeFile "output.txt" (unlines $ show <$> list) }
How can I print an Arbitrary-generated list to a file?
As Test.QuickCheck.Gen tells you, you can use GenT from QuickCheck-GenT. GenT m is a MonadIO instance whenever m is.
main = join . generate . runGenT $ do
let n = 10
list <- liftGen $ vector n
liftIO $ writeFile "output.txt" (unlines $ show <$> list)
seems likely to work.
The vector function gives you the list generator, and not a particular list:
vector :: Arbitrary a => Int -> Gen [a]
Since (>>=) :: Monad m => m a -> (a -> m b) -> m b, it won't get you out of Gen. But generate from Test.QuickCheck.Gen does the particular-value-generation suitable for this situation: generate :: Gen a -> IO a. So generate (vector n) >>= writeFile "output.txt" . unlines . map show should do what you want (save for the the type ambiguity: it's not clear in your example for what Gen [a] your vector will yield, so perhaps add something like (vector n :: Gen [Int]) unless your actual application provides enough context for type inference.

In Haskell, I want to make simple concordancer. But it response errors again, and again

import System.IO
import Data.List
import Data.Char
printlist :: Show a => a -> IO ()
printlist x = putStr (show x)
main = do
handle <- openFile "/usr/local/share/corpus" ReadMode
text <- hGetContents handle
let wlist = words text
clist = map (\k -> take ((k + 15) - k + 1).drop (k - 10))(elemIndices "word" wlist)
printlist clist
What can I do to finish my job.
please, give me a answer or hints
Well I felt nice, so I fixed up the errors here
import Data.List
printlist :: Show a => a -> IO ()
printlist = putStr . show
main = do
text <- readFile "/usr/local/share/corpus" -- removed useless handle
let clist = zipWith (flip ($)) (repeat text)
-- ^ applied each function to file
-- since you currently had
-- clist :: [String -> String]
. map (\k -> take 16 . drop (k-10))
. elemIndices "word"
$ words text -- inlined wlist
printlist clist -- fixed indenting
So now what this does is produce a list of functions of type String -> String and apply each of them to the file /usr/local/share/corpus and print the result.
I suppose the map part can be rewritten to be
(.:) = (.) . (.)
infixr 9 .:
map (take 16 .: drop . subtract 10)
Which is arguably prettier.

Haskell -- problem with pretty-printing a list

I'm new to haskell, and i read through and digested Learn You A Haskell For Great Good, trying out a couple of things along the way. For my first project i wanted to try the classic: FizzBuzz. So i came up with the following code:
import System.IO
fizzBuzz :: (Integral a) => a -> String
fizzBuzz num
| fizz && buzz = "FizzBuzz"
| fizz = "Fizz"
| buzz = "Buzz"
| otherwise = show num
where fizz = num `mod` 3 == 0
buzz = num `mod` 5 == 0
main = print $ map fizzBuzz [1..100]
Worked great, except i got a rather dense looking list that was hard to read. So i tried this main function instead:
main = map putStrLn $ map fizzBuzz [1..100]
And that gives me the error Couldn't match expected type 'IO t' against inferred type '[IO ()]'. I tried half a dozen things and none of it seemed to help. What's the proper way to do what i'm trying to do?
map :: (a -> b) -> [a] -> [b]
putStrLn :: Show a => a -> IO ()
map putStrLn :: Show a => [a] -> [IO ()]
You've got a list of IO () actions.
main :: IO ()
You need to join them into a single IO () action.
What you want to do is to perform each of those IO () actions in sequence/sequence_:
sequence :: Monad m => [m a] -> m [a]
sequence_ :: Monad m => [m a] -> m ()
For convenience, mapM/mapM_ will map a function over a list and sequence the resulting monadic results.
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
So your fixed code would look like this:
main = mapM_ putStrLn $ map fizzBuzz [1..100]
Although I'd probably write it like this:
main = mapM_ (putStrLn . fizzBuzz) [1..100]
Or even this:
main = putStr $ unlines $ map fizzBuzz [1..100]
Let's write our own sequence. What do we want it to do?
sequence [] = return []
sequence (m:ms) = do
x <- m
xs <- sequence ms
return $ x:xs
If there's nothing left in the list, return (inject into the monad) an empty list of results.
Otherwise, within the monad,
Bind (for the IO monad, this means execute) the first result.
sequence the rest of the list; bind that list of results.
Return a cons of the first result and the list of other results.
GHC's library uses something more like foldr (liftM2 (:)) (return []) but that's harder to explain to a newcomer; for now, just take my word that they're equivalent.
sequence_ is easier, since it doesn't bother keeping track of the results. GHC's library implements it as sequence_ ms = foldr (>>) (return ()) ms. Let's just expand the definition of foldr:
sequence [a, b, c, d]
= foldr (>>) (return ()) [a, b, c, d]
= a >> (b >> (c >> (d >> return ())))
In other words, "do a, discard the result; do b; discard the result, … finally, return ()".
mapM f xs = sequence $ map f xs
mapM_ f xs = sequence_ $ map f xs
On the other hand, you don't even need to know monads at all with the alternate unlines solution.
What does unlines do? Well, lines "a\nb\nc\nd\n" = ["a", "b", "c", "d"], so of course unlines ["a", "b", "c", "d"] = "a\nb\nc\nd\n".
unlines $ map fizzBuzz [1..100] = unlines ["1", "2", "Fizz", ..] = "1\n2\nFizz\n..." and off it goes to putStr. Thanks to the magic of Haskell's laziness, the full string never needs to be constructed in memory, so this will happily go to [1..1000000] or higher :)

Resources