2016-04-26 194 views
6

我正在構建一種向用戶顯示對話框的方法。類型推斷 - 無法推斷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)) 

我想用某種形式的「默認」實例的初始化DialogConfigdialog功能,這樣我就可以把它作爲例如defaultConfig{_dialogConfig_content=content}。但是,我正在與類型推理戰鬥。這工作:

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 

然而,當我使用了一些默認DialogConfig(如直接在這裏它內聯),這不:

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 

的錯誤是:

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) 

我可以使用ScopedTypeVariables並鍵入confirmDialog中的默認配置爲DialogConfig t m a b,那可行,但是即使沒有它,它也不能工作嗎?在我看來,類型是相當明確的。

+1

[reflex-dom-contrib]中有一個非常好的(儘管仍然是實驗性的)模態對話框公式(https://github.com/reflex-frp/reflex-dom-contrib/blob/master/src/ Reflex/Dom/Contrib/Widgets/Modal.hs) – user2847643

+0

我們決定暫時自行實施...... – ondra

+3

這些錯誤可能是由於以前的記錄更新中的值未使用,所以它們的類型將會自然會含糊不清。看起來,基本問題是記錄更新可能會改變記錄的類型。要獲得所需的類型推斷,您必須以不改變類型的方式定義更新記錄的某種方式(例如鏡頭)。 – user2407038

回答

4

正如在評論中提到的那樣,問題是記錄更新可以更改記錄的類型(最初可能會令人驚訝)。下面是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 

所以,我們不能定義默認:

> 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 

事實上,上述def可以是任何類型的。

一個可能的解決方案可能是通過要求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 

以上d是,通過建設,必須有更新後的同類型記錄的默認值。

有了鏡頭,可能會有更好的方法。

+0

我會接受這個答案 - 是的,更新可以改變類型的事實是意想不到的,雖然很合理。 – ondra