2
votes

This works:

c <- fmap lines (readFile "d:\\tmp\\h.txt")  
let h = map (read :: String -> Int) c 

while "superposition" of those two lines those not compile

fmap (read :: String -> Int) $ fmap lines (readFile "d:\\tmp\\h.txt")

it generates error:

interactive:1:36:
    Couldn't match expected type `Char' with actual type `[Char]'
    Expected type: String -> String
      Actual type: String -> [String]
    In the first argument of `fmap', namely `lines'
    In the second argument of `($)', namely
      `fmap lines (readFile "d:\\tmp\\h.txt")

Why it does not compile and how to do this in one line? What I want is achieve a simplicity of python

[int(i) for i in open("d:\\tmp\\h.txt")]
3

3 Answers

11
votes

You left the map out of your "superposition" (composition):

h <- fmap (map (read :: String -> Int)) $ fmap lines (readFile "d:\\tmp\\h.txt") 

You can simplify that to

h <- fmap (map (read :: String -> Int) . lines) (readFile "d:\\tmp\\h.txt") 

If you put an import Control.Applicative line at the top of your source file (or enter :m +Control.Applicative if you're using ghci interactively), you can use the <$> operator instead of fmap to make it look cleaner. (They do exactly the same thing, they're just spelled differently.)

h <- map (read :: String -> Int) . lines <$> readFile "d:\\tmp\\h.txt"

Finally, if you do need the type signature, you might find it looks clearer at the end of the line.

h <- map read . lines <$> readFile "d:\\tmp\\h.txt" :: IO [Int]
9
votes
[int(i) for i in open("d:\\tmp\\h.txt")]

Keep computation separate from actions:

return . map read . lines =<< readFile "d:\\tmp\\h.txt"
8
votes

re. your second question: using Applicative would make it more readable:

map read . lines <$> readFile "file"

You may be able to avoid giving read a type signature, depending on the rest of your code, which would be preferable