Some patterns are emerging ("fmap (b->a) . fmap (c->b) . .. . (IO z)"). Also infix zip
is kind-of a hack.
What is:
Best practice in point-free style?
Best practice?
Elegance> Performance; Functional> Imperative
import qualified Data.Map.Lazy as M
import qualified Data.ByteString.Char8 as BS
fromFile :: FilePath -> IO (M.Map Char Int)
fromFile = fmap (M.fromListWith (+)) . fmap (`zip` [1,1..]) . readFile
where readFile = fmap BS.unpack . BS.readFile
1 Answer 1
Best practice would be to separate the pure operations of your program from those that actually require IO. Counting the frequency of elements in a list doesn't require any IO, so you should tease that fromFile
function apart into its constituent components for reusability, testing, comprehensibility, or whatever other purpose you'd like.
frequencies :: Ord k => [k] -> Map k Int
frequencies = fromListWith (+) . (`zip` [1,1..])
fromFile :: FilePath -> IO (Map Char Int)
fromFile = fmap frequencies . fmap unpack . readFile
I'd tweak this just a bit further, using repeat
from the Prelude to build the infinite list instead of abusing list ranges, and to take advantage of the Functor laws and drop a few characters. I flip back and forth on writing functions in pointfree style when it requires infix sectioning too, in this case I'd probably keep the points but I don't know that one choice is clearly better than the other.
frequencies ks = fromListWith (+) $ zip ks (repeat 1)
fromFile = fmap (frequencies . unpack) . readFile