2016-04-26 14 views
6

Ich erstelle eine Möglichkeit, Benutzern einen Dialog anzuzeigen.Typ Rückschluss - Monad konnte nicht abgeleitet werden

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

Ich möchte eine Art von ‚default‘ Instanz verwenden DialogConfig für die dialog Funktion zu initialisieren, so dass ich es als Beispiel verwenden könnte defaultConfig{_dialogConfig_content=content}. Ich kämpfe jedoch mit Typ-Inferenz. Dies funktioniert:

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 

Allerdings, wenn ich einige Standard verwenden DialogConfig (zB hier inlining es direkt), ist es nicht:

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 

Die Fehler sind:

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 kann ScopedTypeVariables verwenden und geben Sie die Standardkonfiguration in confirmDialog als DialogConfig t m a b und das funktioniert, sollte es nicht funktionieren, auch ohne es? Es scheint mir, dass die Typen ziemlich eindeutig sind.

+1

Es gibt eine sehr schöne (wenn auch noch experimentelle) Formulierung von modalen Dialogen in [reflex-dom-contrib] (https://github.com/reflex-frp/reflex-dom-contrib/blob/master/src/ Reflex/Dom/Contrib/Widgets/Modal.hs) – user2847643

+0

Wir haben uns vorerst für eine eigene Implementierung entschieden ... – ondra

+3

Die Fehler sind wahrscheinlich darauf zurückzuführen, dass die Werte in den früheren Datensatzaktualisierungen nicht verwendet werden, also deren Typen natürlich mehrdeutig sein. Das wesentliche Problem scheint zu sein, dass Datensatzaktualisierungen den Typ des Datensatzes ändern können. Um die gewünschte Typinferenz zu erhalten, müssen Sie eine Art der Aktualisierung des Datensatzes (z. B. Objektive) auf eine Weise definieren, die den Typ nicht ändert. – user2407038

Antwort

4

Wie in den Kommentaren erwähnt, das Problem ist, dass Rekord-Update kann Änderung die Art des Datensatzes (die zunächst überraschend sein kann). Hier ist ein Test in 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 

So können wir keine Standard definieren:

> 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 

Tatsächlich oben def jeglicher Art sein könnte.

Eine mögliche Lösung könnte sein, das Update auf den gleichen Typ zu zwingen, indem eine Fortsetzungsfunktion T a -> T a erforderlich ist.

> 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 

Oberhalb d ist die Standardeinstellung, die durch den Bau müssen die gleiche Art des Datensatzes nach dem Update haben.

Mit Objektiven könnte es bessere Ansätze geben.

+0

Ich werde dies als eine Antwort akzeptieren - ja, die Tatsache, dass das Update den Typ ändern kann, war unerwartet, obwohl ziemlich logisch. – ondra

Verwandte Themen