Why does this work in GHCi but not Scotty? - haskell

I am trying to make a webpage that will list the contents of a given directory, but I am running into a strange problem: The code produces the desired output when I run it line by line in GHCi, but when it is executed in the running Scotty instance, it produces different (wrong) output. Here is the relevant part of the code:
serveDir :: String -> ActionM ()
serveDir p = do let path = prefix ++ p
entries <- liftIO $ getDirectoryContents path
fs <- liftIO $ filterM (doesFileExist . ((++) prefix)) entries
ds <- liftIO $ filterM (doesDirectoryExist . ((++) prefix)) entries
liftIO $ print path >> print entries >> print fs >> print ds
blaze $ renderDir fs ds
where prefix = "static/"
(that last line in the do statement just renders it into html. this works, but the correct output never makes it to that function)
When I run each line of this function in GHCi, i get the following output:
*Main> entries <- getDirectoryContents "static/stuff"
*Main> fs <- liftIO $ filterM (doesFileExist . ((++) "static/stuff/")) entries
*Main> ds <- liftIO $ filterM (doesDirectoryExist . ((++) "static/stuff/")) entries
*Main> liftIO $ print entries >> print fs >> print ds
["..","hello","bye","someDir","."]
["hello","bye"]
["..","someDir","."]
which is what I am expecting. But when this function is run from Scotty, called as serveDir "stuff/", I get this output:
"static/stuff/"
["..","hello","bye","someDir","."]
[]
["..","."]
What is happening here? I know that ActionM is an instance of MonadIO, otherwise this would not compile. I am at a bit of a loss. Can somebody shed some light on this or advise? Rest of code available upon request, but I can say that I am using wai-middleware-static to allow static file requests, and other file request from this directory in other functions works.

fs <- liftIO $ filterM (doesFileExist . ((++) prefix)) entries
Should that not be (++) path?

Related

Haskell - getting Char type when expecting [Char] within List Monad

I am practising Haskell by trying to make a program that finds .mp3 and .flac metadata and writes it neatly to a file. I've gone this far on my own but I am pretty stumped at what I should be doing. Here is the main chunk of the code here:
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)
let tagfiles = mapM (tagsort) filedirs
putStrLn $ concat listdirs
putStrLn $ concat tagfiles
tagsort xs = do
nsartist <- getTags xs artistGetter
nsalbum <- getTags xs albumGetter
artist <- init $ drop 8 $ show nsalbum
album <- init $ drop 7 $ show nsalbum
(artist ++ " - " ++ album)
I know, it's very messy. When run in ghci, I get this:
• Couldn't match expected type ‘[Char]’ with actual type ‘Char’
• In the first argument of ‘(++)’, namely ‘artist’
In a stmt of a 'do' block: artist ++ " - " ++ album
In the expression:
do nsartist <- getTags xs artistGetter
nsalbum <- getTags xs albumGetter
artist <- init $ drop 8 $ show nsalbum
album <- init $ drop 7 $ show nsalbum
....
60 artist ++ " - " ++ album
I'm having trouble understanding why this is happening. Running a similar command in a test program of mine:
main = do
artg <- getTags "/home/spilskinanke/backlogtest/02 - await rescue.mp3" artistGetter
let test = init $ drop 8 $ show artg
print test
this works exactly fine. Prints the string "65daysofstatic" to my terminal in ghci. It clearly is not a Char type. So why is being called a Char in my code?
Also note that before adding any pieces of code that referenced the metadata module I am using (htaglib) this program ran fine in a test. With the tagfiles function and tagsort monad absent, I was able to set an arg for a certain directory, and my test would successfully print a list of FilePaths containing all readable folders, and another list of FilePaths containing all files ending in whatever I desired in mlookup, in this case being .mp3, .mp4, .flac, and .wav. Any help would be appreciated.
You’re mixing up IO and [] in tagsort:
tagsort xs = do
-- Okay, run IO action and bind result to ‘nsartist’
nsartist <- getTags xs artistGetter
-- Similarly for ‘nsalbum’
nsalbum <- getTags xs albumGetter
-- Mistaken: ‘init …’ returns a list, not an ‘IO’ action
artist <- init $ drop 8 $ show nsalbum
album <- init $ drop 7 $ show nsalbum
-- You are also missing a ‘pure’ or ‘return’ here
(artist ++ " - " ++ album)
The fixes are simple: use a let statement instead of a bind statement <-, and add a pure to make an IO String out of the String you have:
tagsort xs = do
nsartist <- getTags xs artistGetter
nsalbum <- getTags xs albumGetter
let artist = init $ drop 8 $ show nsalbum
let album = init $ drop 7 $ show nsalbum
pure (artist ++ " - " ++ album)
Generally speaking, each do block must be in a single monad, until you start learning about using monad transformers to combine different effects. So in an IO block, anything on the right of a binding statement must be an IO action; if you just want to do pure computations, you can use let (or just inline expressions, if you don’t need to bind something to a name). Finally, the last statement in a do block must also be an action in the particular monad—this is often a pure value, just wrapped up in the monad with pure :: Applicative f => a -> f a (or return :: Monad m => a -> m a, which does the same thing but works in slightly fewer contexts because of the more restrictive Monad constraint).

Call multiple IO functions on the same input

Suppose I have a list of tuples (e.g. [(a,b)]) each a result of some previous computation.
And I want several functions to be applied on each of these elements (e.g one function might print it another send it over the network etc.)
What I've tried:
import Control.Applicative
main = do
let a = [1..5]
let fs = [(\k-> putStrLn $ show $ k*2), (\k-> putStrLn $ show $ k-2), (\k-> putStrLn $ show $ k*10)]
let res = fs <*> a
putStrLn $ "Yo"
prints just "Yo".
If you look closely res has type [IO ()] and you never use it.
So just sequence it:
main = do
let a = [1..5]
let fs = [(\k-> putStrLn $ show $ k*2), (\k-> putStrLn $ show $ k-2), (\k-> putStrLn $ show $ k*10)]
let res = fs <*> a
sequence res
putStrLn $ "Yo"
in case you want to know how you could right the complete block more concise than you could refactor the list of mappings (using sections), go with print (which is basically your putStrLn . show) and mapM_:
main = do
mapM_ print $ [(* 2), (+ (-2)), (* 10)] <*> [1..5]
putStrLn $ "Yo"
which will give
λ> :main
2
4
6
8
10
-1
0
1
2
3
10
20
30
40
50
Yo
as well ;)
note that you probably should not mix all the IO stuff with the purer computations - instead I would refactor the list of integers out:
myCombinations :: [Int] -> [Int]
myCombinations ns = [(* 2), (+ (-2)), (* 10)] <*> ns
main = do
mapM_ print $ myCombinations [1..5]
putStrLn $ "Yo"
(of course introducing functions as you go along, but I cannot guess what you are trying to achieve here)
from this you gain the ability to just check your pure functions/values:
λ> myCombinations [1..5]
[2,4,6,8,10,-1,0,1,2,3,10,20,30,40,50]
and gain probably a lot of readability ;)
If you have a list of ios :: [a -> IO b] you could use mapM ($ aValue) ios to get IO [b] or mapM_ to get IO ()
let doesn't bind anything into the monad. So IO doesn't care what you do with <*> to apply functions in a list, as long as you don't use the result in any way in a monad action.
To simply execute a list of actions in... well, sequence, you can use sequence:
let res = fs <*> a
sequence res

Reading numbers inline

Imagine I read an input block via stdin that looks like this:
3
12
16
19
The first number is the number of following rows. I have to process these numbers via a function and report the results separated by a space.
So I wrote this main function:
main = do
num <- readLn
putStrLn $ intercalate " " [ show $ myFunc $ read getLine | c <- [1..num]]
Of course that function doesn't compile because of the read getLine.
But what is the correct (read: the Haskell way) way to do this properly? Is it even possible to write this function as a one-liner?
Is it even possible to write this function as a one-liner?
Well, it is, and it's kind of concise, but see for yourself:
main = interact $ unwords . map (show . myFunc . read) . drop 1 . lines
So, how does this work?
interact :: (String -> String) -> IO () takes all contents from STDIN, passes it through the given function, and prints the output.
We use unwords . map (show . myFunc . read) . drop 1 . lines :: String -> String:
lines :: String -> [String] breaks a string at line ends.
drop 1 removes the first line, as we don't actually need the number of lines.
map (show . myFunc . read) converts each String to the correct type, uses myFunc, and then converts it back to a `String.
unwords is basically the same as intercalate " ".
However, keep in mind that interact isn't very GHCi friendly.
You can build a list of monadic actions with <$> (or fmap) and execute them all with sequence.
λ intercalate " " <$> sequence [show . (2*) . read <$> getLine | _ <- [1..4]]
1
2
3
4
"2 4 6 8"
Is it even possible to write this function as a one-liner?
Sure, but there is a problem with the last line of your main function. Because you're trying to apply intercalate " " to
[ show $ myFunc $ read getLine | c <- [1..num]]
I'm guessing you expect the latter to have type [String], but it is in fact not a well-typed expression. How can that be fixed? Let's first define
getOneInt :: IO Int
getOneInt = read <$> getLine
for convenience (we'll be using it multiple times in our code). Now, what you meant is probably something like
[ show . myFunc <$> getOneInt | c <- [1..num]]
which, if the type of myFunc aligns with the rest, has type [IO String]. You can then pass that to sequence in order to get a value of type IO [String] instead. Finally, you can "pass" that (using =<<) to
putStrLn . intercalate " "
in order to get the desired one-liner:
import Control.Monad ( replicateM )
import Data.List ( intercalate )
main :: IO ()
main = do
num <- getOneInt
putStrLn . intercalate " " =<< sequence [ show . myFunc <$> getOneInt | c <- [1..num]]
where
myFunc = (* 3) -- for example
getOneInt :: IO Int
getOneInt = read <$> getLine
In GHCi:
λ> main
3
45
23
1
135 69 3
Is the code idiomatic and readable, though? Not so much, in my opinion...
[...] what is the correct (read: the Haskell way) way to do this properly?
There is no "correct" way of doing it, but the following just feels more natural and readable to me:
import Control.Monad ( replicateM )
import Data.List ( intercalate )
main :: IO ()
main = do
n <- getOneInt
ns <- replicateM n getOneInt
putStrLn $ intercalate " " $ map (show . myFunc) ns
where
myFunc = (* 3) -- replace by your own function
getOneInt :: IO Int
getOneInt = read <$> getLine
Alternatively, if you want to eschew the do notation:
main =
getOneInt >>=
flip replicateM getOneInt >>=
putStrLn . intercalate " " . map (show . myFunc)
where
myFunc = (* 3) -- replace by your own function

haskell read from file

i'm trying to read from file in haskell and process every line
what i have now
main = interact (unlines . (map calculate) . lines)
this let me get every line from input and send it to calculate
now i want to get every line from a file and send it to calculate
this is what i tried
main = do
text <- readFile "input.txt"
let linii = lines text
interact (unlines . (map calculate) . linii)
tell me please how is it correct?
UPDATE below
calculate :: String -> String
calculate s=
case ret of
Left e -> "error: " ++(show e)
Right n -> "answer: " ++ (show n)
where
ret = parse parseInput "" s
main :: IO()
--main = interact (unlines . (map calculate) . lines)
main = do text <- readFile "input.txt"
let linii = lines
putStrLn . unlines $ map calculate linii
Remember that interact takes input from stdin and sends output to stdout. Since you have already read input from a file, you don't need the former. You only need to do the later. You can print a String with putStrln. Putting this all together, change
interact (unlines . (map calculate) . linii)
to
putStrLn . unlines $ map calculate linii

Operation on user getLine result before storing in variable (Haskell)

this is my code:
askPointer = do
input <- getLine
let newInput = map toUpper input
[..here I will re-use new Input..]
return ()
Is it possible (maybe using lamba notation), to make this code shorter in one line only?
My attempt has been unsuccessfully:
input <- (\a b-> do toUpper (b <- getLine ) )
Any suggest?
Edit: little edit to make this question looking for more generic answers (not limiting to returning functions)
Applying a function to the result of an IO operation before using it is an excellent description of what fmap does.
askPointer = do
newInput <- fmap (map toUpper) getLine
[..here I will re-use new Input..]
return ()
So here fmap does exactly what you wanted - it applies map toUpper to the result of getLine before you bind that to newInput.
Try these out in your interpreter (ghci/hugs):
fmap reverse getLine
fmap tail getLine
fmap head getLine
fmap (map toUpper) getLine
If you import Data.Functor or import Control.Applicative, you can use the infix version of fmap, <$>:
reverse <$> getLine
tail <$> getLine
head <$> getLine
map toUpper <$> getLine
which means you could also write
askPointer = do
newInput <- map toUpper <$> getLine
[..here I will re-use new Input..]
return ()
fmap is a very very useful function indeed to know. You could read more in this other answer about fmap where I ended up writing a mini tutorial.
This should work:
askPointer = getLine >>= return . map toUpper
If you import Control.Applicative you can make it even shorter:
askPointer = map toUpper <$> getLine
Considering last edit:
input <- getLine >>= return . map toUpper
or
input <- map toUpper <$> getLine

Resources