10 votes

Comment ne pas appliquer de contraintes d'instance à une fonction restreinte qui utilise une fonction plus générique ?

Disons que j'ai une fonction :

logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()
logResult = ...

Dans cette fonction, si j'obtiens :

  1. Right (Response a) - J'appelle toJSON pour enregistrer le résultat.
  2. Left MyError - Je l'enregistre également. MyError dispose déjà d'un ToJSON définie.

Je veux maintenant écrire une fonction d'aide :

logError :: (MonadIO m) :: MyError -> m ()
logError err = logResult (Left err)

Mais GHC se plaint de quelque chose qui ressemble à ce qui suit :

    • Could not deduce (ToJSON a0) arising from a use of ‘logResult’                                                                    
      from the context: MonadIO m                                                                                                       
        bound by the type signature for:                                                                                                
                   logError :: forall (m :: * -> *).                                                                                    
                               MonadIO m =>                                                                                             
                               L.Logger                                                                                                 
                               -> Wai.Request
                               -> MyError
                               -> m ()

...
...
The type variable ‘a0’ is ambiguous 

Je comprends que l'erreur est due au fait que logResult doit garantir que le a en Response a doit disposer d'un ToJSON définie. Mais en logError Je transmets explicitement Left MyError . Cela ne devrait-il pas désambiguïser ?

Existe-t-il un moyen d'écrire le logError fonction d'aide ?

PS : J'ai simplifié les signatures de type dans l'exemple. Le message d'erreur contient tous les détails.

11voto

HTNW Points 9343

Pourquoi cette fonction ? Si le comportement de cette fonction se divise si proprement en deux, alors elle devrait être deux fonctions. En d'autres termes, vous avez écrit une fonction monolithique et vous essayez de définir une fonction plus simple en tant qu'utilitaire qui l'utilise. Au lieu de cela, écrivez une fonction simple et écrivez la fonction monolithique comme une composition de celle-ci avec une autre. Le type le demande : Either a b -> c est isomorphe à (a -> c, b -> c) .

-- you may need to factor out some common utility stuff, too
logError :: (MonadIO m) :: MyError -> m ()
logResponse :: (MonadIO m, ToJSON a) => Response a -> m ()

logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()
logResult = either logError logResponse

logResult a encore son utilité ; si vous obtenez un Either MyError (Response a) d'une bibliothèque, puis logResult peut s'en occuper sans trop d'états d'âme. Mais sinon, vous ne devriez pas écrire logResult (Left _) o logResult (Right _) très souvent ; qui traite essentiellement logResult . Left y logResult . Right comme leurs propres fonctions, ce qui vous ramène à les écrire comme des fonctions distinctes.

Mais en logError Je transmets explicitement Left MyError . Ne devrait-il pas y avoir une désambiguïsation ?

Non, cela ne devrait pas être le cas. La fin et le début de la question sont les suivants logResult ressemble à ceci :

logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()

Lorsque vous l'appelez, la mise en œuvre n'a pas d'importance. Le type dit que vous avez besoin de ToJSON a -vous devez fournir ToJSON a . C'est cela. Si vous savez que vous n'avez pas besoin de ToJSON a para Left vous possédez des informations utiles qui ne sont pas reflétées dans le type. Vous devez ajouter ces informations au type, ce qui, dans ce cas, signifie le diviser en deux. Ce serait (IMO) en fait une mauvaise conception du langage que de permettre ce à quoi vous pensiez, parce que le problème de halte devrait rendre cela impossible à faire correctement.

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