类型推断 - 无法推断出Monad

时间:2016-04-26 08:23:41

标签: haskell reflex

我正在构建一种向用户显示对话框的方法。

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

我想使用某种'默认'实例初始化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

但是,当我使用某些默认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}}的默认配置,但是如果没有它,它是否可以正常工作?在我看来,这些类型是相当明确的。

1 个答案:

答案 0 :(得分: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是默认值,按构造,在更新后必须具有相同类型的记录。

使用镜头,可能会有更好的方法。