I often find myself having to explicitly specify return
to wrap an expression in IO/Maybe/Either
, etc... but I'm sure there must be a better way to make it point-free.
For example:
countLines :: FilePath -> IO Int countLines file = readFile file >>= countLines where countLines = (\\d -> return $ (length . lines) d)
Is there a way to achieve a point-free style for the countLines
function? Writing lambdas for each step of the >>=
is tiresome.
countLines :: FilePath -> IO Int
countLines file = readFile file >>= countLines
where countLines = (\d -> return $ (length . lines) d)
By the way, it is not a good practice to use the same name of the function in a where
clause: the function looks recursive even if it is not.
Back to the question, the code above means
countLines file = readFile file >>= (\d -> return $ (length . lines) d)
which means
countLines file = readFile file >>= (return . (length . lines))
which can be written as
countLines file = length . lines <$> readFile file
-- or
countLines file = fmap (length . lines) $ readFile file
One can go further and also remove the file
point, but readability would be slightly harmed.
countLines = fmap (length . lines) . readFile
Is there a way to achieve a point-free style for the countLines function? Writing lambdas for each step of the >>= is tiresome.
Well, pointfree style is hardly more readable in the general case. Why don't you use do notation instead?
countLines :: FilePath -> IO Int
countLines file = do
d <- readFile file
return . length . lines $ d
This is not as elegant as the pointfree-ish variant in this case, but is much more general. Even when chaining many >>=
s sometimes the following style is used:
foo =
m1 >>= \x1 ->
m2 >>= \x2 ->
m3 >>= \x3 ->
...
mN >>= \xN ->
return (...)
To make your function here
countLines :: FilePath -> IO Int
countLines file = readFile file >>= countLines
where countLines = (\d -> return $ (length . lines) d)
pointfree, we need to first refactor it to this:
countLines :: FilePath -> IO Int
countLines file = readFile file >>= countLines
where countLines = (\d -> return $ (length . lines) $ d)
Now, we can compose the return with the other functions, like so (we can remove the parenthesis):
countLines :: FilePath -> IO Int
countLines file = readFile file >>= countLines
where countLines = (\d -> return . length . lines $ d)
Now we can take out the point (again, remove parenthesis):
countLines :: FilePath -> IO Int
countLines file = readFile file >>= countLines
where countLines = return . length . lines
Now the function can be inlined:
countLines :: FilePath -> IO Int
countLines file = readFile file >>= return . length . lines
As an added bonus, if you want to make countLines totally pointfree, you can use the Kleisi composition operator (found in Control.Monad
) instead of bind, like so:
countLines :: FilePath -> IO Int
countLines = readFile >=> return . length . lines
although it is arguable that the other version is more readable.
The last one can also be written like:
countLines :: FilePath -> IO Int
countLines = return . length . lines <=< readLine
which has the advantage that you can read the function from right to left and see exactly how the function "flows".
Your code
countLines file = readFile file >>= countLines
where countLines = (\d -> return $ (length . lines) d)
contains the anonymous function
\d -> return $ (length . lines) d
which is equivalent to
\d -> return ((length . lines) d)
and the point d
can be removed using function composition to give
return . (length . lines)
This allows you to write
countLines :: FilePath -> IO Int
countLines file = readFile file >>= return . (length . lines)
The pattern a >>= return . f
a >>= return . f
is equivalent to fmap fa
, so you can also write
countLines :: FilePath -> IO Int
countLines file = fmap (length . lines) (readFile file)
and you can now remove the point file
using function composition as well,
countLines :: FilePath -> IO Int
countLines = fmap (length . lines) . readFile
Personally I find this more readable than the original, but opinions may differ.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.