Сокращение удовлетворенных ограничений для обычных типов - PullRequest
0 голосов
/ 06 мая 2018

Я понимаю, что следующее семейство типов не должно и, возможно, не может быть реализовано в GHC:

type family MatchesConstraint c a :: Bool where
  MatchesConstraint c a is True if (c a)
  MatchesConstraint c a is False otherwise

Это проблематично, потому что классы открыты, поэтому MatchesConstraint c a может оценить до True для некоторых частей программы и False в других в зависимости от того, какие экземпляры находятся в области видимости, что, я думаю, может быть довольно катастрофическим.

Но учтите следующее:

type family MatchesConstraint c a :: Bool where
  MatchesConstraint c a is True if (c a)
  MatchesConstraint c a doesn't reduce otherwise

Это кажется довольно безопасным. В некоторых частях нашей программы мы можем не уменьшить, если экземпляр не находится в области видимости, но у нас никогда не будет несоответствия.

Могу ли я сделать что-то подобное в GHC?

Причина, по которой я спрашиваю об этом, заключается в том, что, возможно, можно выбирать экземпляры, основываясь не только на типе напрямую, но и на классе. Что может быть полезным в некоторых контекстах, я верю.

1 Ответ

0 голосов
/ 07 мая 2018

Я ответил на аналогичный вопрос от вас здесь, в кафе .

Как говорит @Carl, все экземпляры должны находиться везде в области видимости. Исключением является компиляция так называемых 'сиротских экземпляров' - что является плохой вещью и ее легко избежать.

Для записи здесь, подход заключается в использовании Ассоциированного типа в вашем классе с определением по умолчанию. Это работает, если вы искренне рады, что тип Associated сгенерировал ошибку «no instance», если нет соответствия ограничению:

class A t where
  type MatchesA t :: Bool           -- Associated type
  type instance MatchesA t = True   -- default instance
  ...                               -- methods for A

instance A Int where
                                    -- uses default instance for MatchesA
  ...                               -- method implementations as usual

-- undefined :: (MatchesA Int)      -- gives type True
-- undefined :: (MatchesA Bool)     -- gives 'no instance' error

Я думаю, вы можете получить ограничение - или Matches - см. Сообщение в кафе для MatchesA или MatchesB. (Я быстро это проверил, это может быть немного странно, в зависимости от того, насколько нетерпеливым является сокращение семейства типов.)

То, что вы не можете сделать с этим подходом, - это выбрать одну вещь, если ограничение выполняется, и другую, если это не так. Таким образом, лучшее, что вы можете получить, это «не уменьшить». В посте кафе я ссылаюсь на (довольно старую) вики-страницу с более всесторонним подходом, который опирается на перекрывающиеся экземпляры. Связанные типы не допускают перекрытий: - (.

РЕДАКТИРОВАТЬ: Существует глубокая причина, по которой компилятор не показывает, соответствует ли какое-либо ограничение, как вы хотите для Prelude классов в вашем комментарии: вы можете использовать факт соответствия с выберите какой-нибудь экземпляр другого класса C; и может существовать еще один класс D, выбор экземпляра которого зависит от совпадения C; и выбор экземпляра A, B зависит от D. Так что теперь у нас есть круговые зависимости. Это может быть осложнено другими экземплярами в других модулях, которые компилятор еще не видел.

Таким образом, все эти (очевидно) дублированные экземпляры на вики-странице представляют, что программист знает все экземпляры и как они взаимозависимы, таким образом, что компилятор не может понять.

...