当参数类型不是静态时,如何分解Haskell语句?

时间:2017-05-21 16:09:10

标签: haskell

这有点难以解释,但我已经遇到过几次这种情况。

代码如下:

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')

以上引发错误:

• 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)

哪个有道理,但是有办法以某种方式实现上述目标吗?否则我必须做下面的事情,感觉不太可维护:

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 个答案:

答案 0 :(得分:6)

您正在遇到Dreaded Monomorphism Restriction。由于优化和代码生成的问题,当GHC看到一个没有显式参数的值时,它会推断出它的单态类型,而不是你期望的更一般的多态类型。

您可以使用NoMonomorphismRestriction pragma禁用限制,也可以为f提供明确的参数:

sortF <- let f xs = sortByM xs input in ...

答案 1 :(得分:1)

对你想要的东西做一些假设......在代码结构上往往有很多选择。你可以有一个总和类型(这里不是一个很好的解决方案):

  ...
  sortF <- let f = flip sortByM input
    in case a of
        "name" -> Left <$> f (return . id         :: FilePath -> IO FilePath)
        "time" -> Right <$> f (getModificationTime :: FilePath -> IO UTCTime)
        _ ->      Right <$> f (getModificationTime :: FilePath -> IO UTCTime)
  either print print sortF

因为您真正了解“多类型”sortF的唯一事实是它是Show的一个实例,您只需拨打show并使用String即可:

  ...
  sortF <- let f = flip sortByM input
    in show <$> case a of
        "name" -> f (return . id         :: FilePath -> IO FilePath)
        "time" -> f (getModificationTime :: FilePath -> IO UTCTime)
        _ ->      f (getModificationTime :: FilePath -> IO UTCTime)
  putStrLn sortF

您可以使用更加面向功能的方法并将类型统一到String

  ...
  let op | a == "name" = return . show
         | otherwise   = fmap show . getModificationTime
      f x = sortByM x input
  putStrLn =<< f =<< op