How can I break up a Haskell statement when the type of a parameter is not static?

Viewed 108

This is a bit difficult to explain, but I've run into this situation a few times.

The code is as follows:

work :: String -> IO ()
work a = do
  input <- lines <$> getContents
  sortF <- let f = flip sortByM input
    in case a of
        "name" -> f (return . id         :: FilePath -> IO FilePath)
        "time" -> f (getModificationTime :: FilePath -> IO UTCTime)
        _ ->      f (getModificationTime :: FilePath -> IO UTCTime)
  print sortF

sortByM :: (Monad m, Ord a) => (b-> m a) -> [b] -> m [b]
sortByM f x = do
  x' <- mapM f x
  return $ fst <$> (sortBy (comparing snd) $ zip x x')

The above throws an error of:

• Couldn't match type ‘UTCTime’ with ‘[Char]’
  Expected type: String -> IO FilePath
    Actual type: FilePath -> IO UTCTime
• In the first argument of ‘f’, namely
    ‘(getModificationTime :: FilePath -> IO UTCTime)’
  In the expression:
    f (getModificationTime :: FilePath -> IO UTCTime)
  In a case alternative:
      "time" -> f (getModificationTime :: FilePath -> IO UTCTime)

Which makes sense, but is there a way to somehow achieve the above? Otherwise I have to do the below, which feels less maintainable:

work :: String -> IO ()
work a = do
  input <- lines <$> getContents
  sortF <- case a of
        "name" -> flip sortByM input (return . id         :: FilePath -> IO FilePath)
        "time" -> flip sortByM input (getModificationTime :: FilePath -> IO UTCTime)
        _ ->      flip sortByM input (getModificationTime :: FilePath -> IO UTCTime)
  print sortF

sortByM :: (Monad m, Ord a) => (b-> m a) -> [b] -> m [b]
sortByM f x = do
  x' <- mapM f x
  return $ fst <$> (sortBy (comparing snd) $ zip x x')
2 Answers
Related