Вывод типа - не удалось вывести монаду

Я создаю способ отображения диалога для пользователей.

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, и это работает, однако разве это не должно работать даже без него? Мне кажется, что типы достаточно однозначны.


person ondra    schedule 26.04.2016    source источник
comment
Существует очень хорошая (хотя все еще экспериментальная) формулировка модальных диалогов в reflex-dom-contrib   -  person user2847643    schedule 26.04.2016
comment
Мы решили для нашей собственной реализации на данный момент...   -  person ondra    schedule 26.04.2016
comment
Ошибки, вероятно, связаны с тем, что значения в прежних обновлениях записей не используются, поэтому их типы, естественно, будут неоднозначными. Существенная проблема, по-видимому, заключается в том, что обновления записей могут изменить тип записи. Чтобы получить желаемый вывод типа, вам нужно определить какой-либо способ обновления записи (например, линзы) таким образом, чтобы тип не менялся.   -  person user2407038    schedule 26.04.2016


Ответы (1)


Как упоминалось в комментариях, проблема заключается в том, что обновление записи может изменить тип записи (что поначалу может удивить). Вот тест в 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 — это значение по умолчанию, которое по своей конструкции должно иметь тот же тип записи после обновления.

С линзами могут быть лучшие подходы.

person chi    schedule 27.04.2016
comment
Приму это как ответ - да, тот факт, что обновление может изменить тип, было неожиданным, хотя и вполне логичным. - person ondra; 27.04.2016