2016-04-26 7 views
6

Je suis en train de créer un moyen d'afficher une boîte de dialogue pour les utilisateurs.Inférence de type - impossible de déduire Monad

data DialogConfig t m b e = 
    DialogConfig { _dialogConfig_title :: Dynamic t T.Text 
       , _dialogConfig_content :: b -> m (Dynamic t (Maybe b)) 
       , _dialogConfig_footer :: Dynamic t (Maybe b) -> m (Event t e) 
       } 
dialog :: MonadWidget t m => 
      DialogConfig t m b e -> Event t b -> m (Event t (DialogEvent e)) 

Je voudrais utiliser une sorte d'instance « par défaut » pour initialiser DialogConfig pour la fonction dialog, pour que je puisse l'utiliser comme par exemple defaultConfig{_dialogConfig_content=content}. Cependant, je me bats avec l'inférence de type. Cela fonctionne:

confirmDialog :: forall t m. MonadWidget t m => 
       T.Text -> Event t T.Text -> m (Event t()) 
... 
evt <- dialog 
     (DialogConfig { _dialogConfig_title = constDyn title 
         , _dialogConfig_content = content 
         , _dialogConfig_footer = buttons} 
         ) contentEvt 

Cependant, quand je l'utilise par défaut une DialogConfig (par exemple ici directement inline), il ne fait pas:

evt <- dialog 
     (DialogConfig { _dialogConfig_title = constDyn mempty 
        , _dialogConfig_content = const $ return $ constDyn Nothing 
        , _dialogConfig_footer = const $ return never } 
        { _dialogConfig_title = constDyn title 
        , _dialogConfig_content = content 
        , _dialogConfig_footer = buttons} 
        ) contentEvt 

Les erreurs sont les suivantes:

Could not deduce (Reflex t0) arising from a use of ‘constDyn’ from the context (MonadWidget t m) 
Could not deduce (Monad t1) arising from a use of ‘return’ from the context (MonadWidget t m) 

I peut utiliser ScopedTypeVariables et tapez la configuration par défaut dans le confirmDialog comme DialogConfig t m a b et cela fonctionne, mais ne devrait pas fonctionner même sans elle? Il me semble que les types sont plutôt non ambigus.

+1

Il existe une très belle formulation (bien que toujours expérimentale) de dialogues modaux dans [reflex-dom-contrib] (https://github.com/reflex-frp/reflex-dom-contrib/blob/master/src/ Reflex/Dom/Contrib/Widgets/Modal.hs) – user2847643

+0

Nous avons décidé pour notre propre implémentation pour l'instant ... – ondra

+3

Les erreurs sont probablement dues au fait que les valeurs dans les mises à jour de l'ancien enregistrement sont inutilisées, donc leurs types seront naturellement être ambigu. Le problème essentiel, semble-t-il, est que les mises à jour d'enregistrements peuvent changer le type de l'enregistrement. Pour obtenir l'inférence de type que vous voulez, vous devrez définir un moyen de mettre à jour l'enregistrement (par exemple, des lentilles) d'une manière qui ne change pas le type. – user2407038

Répondre

4

Comme mentionné dans les commentaires, le problème est que la mise à jour d'enregistrement peut changer le type de l'enregistrement (ce qui peut être surprenant au début). Voici un test GHCi:

> data T a = T { tA :: a } 
> let x = T "foo" 
> :t x 
x :: T [Char] 
> :t x { tA = True } 
x { tA = True } :: T Bool 

Donc, nous ne pouvons pas définir une valeur par défaut:

> let def :: Read a => T a ; def = T (read "True") 
> :t def :: T Bool 
def :: T Bool :: T Bool 
> :t def { tA = 5 } 
    Could not deduce (Read t0) arising from a use of ‘def’ 
    The type variable ‘t0’ is ambiguous 

En effet, au-dessus def pourrait être de tout type.

Une solution possible pourrait être de forcer la mise à jour à avoir le même type en exigeant une fonction de continuation T a -> T a.

> let defF :: Read a => (T a -> T a) -> T a ; defF f = f (T (read "True")) 
> :t defF (\d -> d { tA = False }) 
defF (\d -> d { tA = False }) :: T Bool 

Au-dessus d est la valeur par défaut qui, par construction, doivent avoir le même type d'enregistrement après la mise à jour.

Avec des lentilles, il pourrait y avoir de meilleures approches.

+0

Je vais accepter cela comme une réponse - oui, le fait que la mise à jour peut changer le type était inattendu bien que tout à fait logique. – ondra