4 votes

Comment puis-je découper une déclaration Haskell lorsque le type d'un paramètre n'est pas statique?

C'est un peu difficile à expliquer, mais j'ai rencontré cette situation plusieurs fois.

Le code est le suivant :

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

L'erreur ci-dessus est la suivante :

• Impossible d'associer le type ‘UTCTime’ avec ‘[Char]’
  Type attendu: String -> IO FilePath
    Type actuel : FilePath -> IO UTCTime
• Dans le premier argument de ‘f’, à savoir
    ‘(getModificationTime :: FilePath -> IO UTCTime)’
  Dans l'expression :
    f (getModificationTime :: FilePath -> IO UTCTime)
  Dans une alternative de cas :
      "time" -> f (getModificationTime :: FilePath -> IO UTCTime)

Cela a du sens, mais y a-t-il un moyen d'obtenir le résultat souhaité d'une certaine manière ? Sinon, je devrai compléter comme ci-dessous, ce qui semble moins maintenable :

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

6voto

Paul Johnson Points 8604

Vous rencontrez la redoutée restriction de monomorphisme. En raison de problèmes liés à l'optimisation et à la génération de code, lorsque GHC voit une valeur qui n'a pas d'arguments explicites, il en déduira un type monomorphe plutôt que le type polymorphe plus général que vous attendiez.

Vous pouvez soit désactiver la restriction en utilisant la directive NoMonomorphismRestriction, soit donner un paramètre explicite à f:

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

1voto

Thomas M. DuBuisson Points 31851

En faisant quelques hypothèses sur ce que vous voulez... il y a tendance à avoir beaucoup d'options sur la structure du code. Vous pouvez avoir un type de somme (pas une bonne solution ici) :

  ...
  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

Étant donné que la seule chose que vous savez vraiment sur un sortF "multi-typé" est qu'il est une instance de Show, vous pouvez simplement appeler show et utiliser 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

Et vous pouvez utiliser une approche plus orientée fonctionnellement en combinant avec l'unification du type en String :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X