Search code examples
haskellreflex

Type inference - could not deduce Monad


I am building a way to display a dialog to users.

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

I would like to use some kind of 'default' instance to initialize DialogConfig for the dialog function, so that I could use it as e.g. defaultConfig{_dialogConfig_content=content}. However, I'm fighting with type inferrence. This works:

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

However, when I use some default DialogConfig (e.g. here directly inlining it), it doesn't:

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

The errors are:

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 can use ScopedTypeVariables and type the default config in the confirmDialog as DialogConfig t m a b and that works, however shouldn't it work even without it? It seems to me that the types are rather unambiguous.


Solution

  • As mentioned in the comments, the problem is that record update can change the type of the record (which can be surprising at first). Here's a 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, we can't define a default:

    > 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
    

    Indeed, above def could be of any type.

    A possible solution could be forcing the update to have the same type by requiring a T a -> T a continuation function.

    > 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
    

    Above d is the default which, by construction, must have the same type of the record after the update.

    With lenses, there might be better approaches.