我正在构建一种向用户显示对话框的方法.
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))
Run Code Online (Sandbox Code Playgroud)
我想用某种形式的"默认"实例的初始化DialogConfig的dialog功能,这样我就可以使用它作为如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
Run Code Online (Sandbox Code Playgroud)
但是,当我使用某些默认值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
Run Code Online (Sandbox Code Playgroud)
错误是:
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)
Run Code Online (Sandbox Code Playgroud)
我可以使用ScopedTypeVariables和输入confirmDialogas中的默认配置DialogConfig t m a b,但是如果没有它,它应该不工作吗?在我看来,这些类型是相当明确的.
正如评论中提到的,问题在于记录更新可以更改记录的类型(一开始这可能会令人惊讶)。这是 GHCi 中的测试:
\n\n> data T a = T { tA :: a }\n> let x = T "foo"\n> :t x\nx :: T [Char]\n> :t x { tA = True }\nx { tA = True } :: T Bool\nRun Code Online (Sandbox Code Playgroud)\n\n因此,我们无法定义默认值:
\n\n> let def :: Read a => T a ; def = T (read "True")\n> :t def :: T Bool\ndef :: T Bool :: T Bool\n> :t def { tA = 5 }\n Could not deduce (Read t0) arising from a use of \xe2\x80\x98def\xe2\x80\x99\n The type variable \xe2\x80\x98t0\xe2\x80\x99 is ambiguous\nRun Code Online (Sandbox Code Playgroud)\n\n事实上,上面def可以是任何类型。
一种可能的解决方案是通过需要延续函数来强制更新具有相同的类型T a -> T a。
> let defF :: Read a => (T a -> T a) -> T a ; defF f = f (T (read "True"))\n> :t defF (\\d -> d { tA = False })\ndefF (\\d -> d { tA = False }) :: T Bool\nRun Code Online (Sandbox Code Playgroud)\n\n以上d是默认值,根据构造,更新后必须具有相同类型的记录。
有了镜头,可能会有更好的方法。
\n