Как объединить два ограничения типа с логическим или в Haskell?

в Haskell нам предоставляется возможность комбинировать ограничения по типам с логическим И.

рассмотрим следующее

type And (a :: Constraint) b = (a, b)

или более изощренно -

class (a, b) => And a b
instance (a, b) => And a b

Я хочу знать, как логически или два ограничения вместе в Haskell.

моя ближайшая попытка-это, но она не совсем работает. В этой попытке я reify ограничения типа с тегами и чем dereify их с неявными параметрами.

data ROr a b where
 L :: a => ROr a b
 R :: b => ROr a b

type Or a b = (?choose :: ROr a b)

y :: Or (a ~ Integer) (Bool ~ Integer) => a
y = case ?choose of
 L -> 4

x :: Integer
x = let ?choose = L in y

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

как я могу логически или два ограничения вместе?

2 ответов


Я считаю, что нет никакого способа, чтобы автоматически выбрать ROr a b; это нарушило бы предположение открытого мира, если, например,b было удовлетворено, но позже a также был удовлетворен; любое правило разрешения конфликтов обязательно приведет к добавлению экземпляра для изменения поведения существующего кода.

то есть, взяв R, когда b удовлетворен, но a не нарушает предположение открытого мира, потому что оно включает в себя решение о том, что экземпляр не удовлетворены;1 даже если вы добавили конструктор" оба удовлетворены", вы сможете использовать его, чтобы решить, является ли экземпляр не present (увидев, если вы получите L или R).

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

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


Я пришел сюда, чтобы ответить на ваш вопрос о кафе. Не уверен, что q здесь то же самое, но в любом случае ...

класс типа с тремя параметрами.

   class Foo a b c | a b -> c where
     foo :: a -> b -> c
   instance Foo A R A where ...
   instance Foo R A A where ...

в дополнение к функциональной зависимости я хотел бы выразить, что по крайней мере один из параметров a и b-c,

import Data.Type.Equality
import Data.Type.Bool

class ( ((a == c) || (b == c)) ~ True)
      => Foo a b c  | a b -> c  where ...

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

ваш q здесь

как я могу логически или два ограничения вместе?

гораздо сложнее. Для подхода равенства типов == использует семейство закрытого типа. Таким образом, вы можете написать семейство закрытого типа, возвращающее вид Constraint, но я сомневаюсь, что есть общее решение. Для Foo класс:

type family AorBeqC a b c :: Constraint where
  AorBeqC a b a = ()
  AorBeqC a b c = (b ~ c)

class AorBeqC a b c => Foo a b c  | a b -> c  where ...

вероятно, у него будет плохое и несимметричное поведение улучшения типа: если GHC может видеть это a, c отдельно, он перейдет ко второму уравнению и использует (b ~ c) чтобы улучшить либо; если он не видит, что они разделены или что они унифицированы, он застрянет.

в общем, как указывает @ehird, вы не можете проверить, является ли ограничение не satisfiable. Равенства типа особенный.