Some patterns are emerging ("fmap (b->a) . fmap (c->b) . .. . (IO z)"). Also infix zip is kind-of a hack.

What is:

  1. Best practice in point-free style?

  2. 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
  • \$\begingroup\$ Need 2 reps to Up you:). I agree on IO separation and repeat. \$\endgroup\$
    – xged
    Aug 30, 2014 at 20:25
  • \$\begingroup\$ @xged you got it! \$\endgroup\$
    – Will Ness
    Sep 3, 2014 at 13:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.