Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type inference - could not deduce Monad

Tags:

haskell

reflex

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.

like image 401
ondra Avatar asked Apr 26 '16 08:04

ondra


1 Answers

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.

like image 196
chi Avatar answered Oct 22 '22 09:10

chi