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')
You are running into the Dreaded Monomorphism Restriction. Due to issues with optimisation and code generation, when GHC sees a value that doesn't have explicit arguments it will infer a monomorphic type for it rather than the more general polymorphic type you would expect.
You can either disable the restriction using the NoMonomorphismRestriction
pragma, or you can give f
an explicit parameter:
sortF <- let f xs = sortByM xs input in ...
Making some assumptions about what you want... there tends to be lots of options on code structure. You can have a sum type (not a great solution here):
...
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
Since the only thing you really know about a "multi-typed" sortF
is that it is an instance of Show
you can just call show
and use 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
And you can use a more function-oriented approach in combination with unifying the type to String
:
...
let op | a == "name" = return . show
| otherwise = fmap show . getModificationTime
f x = sortByM x input
putStrLn =<< f =<< op
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With