Я играю с пакетом constraints
(для GHC Haskell). У меня есть семейство типов для определения того, содержит ли список уровня типа элемент:
type family HasElem (x :: k) (xs :: [k]) where
HasElem x '[] = False
HasElem x (x ': xs) = True
HasElem x (y ': xs) = HasElem x xs
Это работает, но одна вещь, которая мне не дает, - это знание, что
HasElem x xs entails HasElem x (y ': xs)
поскольку семейство типов не является индуктивным определением оператора "является элементом" (как и в agda). Я уверен, что до тех пор, пока GADT не будут продвигаться к типу уровня, нет способа выразить членство в списке с типом данных.
Итак, я использовал пакет constraints
, чтобы написать это:
containerEntailsLarger :: Proxy x -> Proxy xs -> Proxy b -> (HasElem x xs ~ True) :- (HasElem x (b ': xs) ~ True)
containerEntailsLarger _ _ _ = unsafeCoerceConstraint
Призрачный, но он работает. Я могу составить совпадение по желанию, чтобы получить то, что мне нужно. Мне интересно, может ли это привести к сбою программы. Кажется, что он не мог, так как unsafeCoerceConstraint
определяется как:
unsafeCoerceConstraint = unsafeCoerce refl
И в GHC уровень уровня удаляется во время выполнения. Я думал, что проверю хотя бы, чтобы убедиться, что все в порядке.
--- EDIT ---
Поскольку никто еще не дал объяснений, я подумал, что немного перейду к вопросу. В небезопасном вступлении, которое я создаю, я ожидаю только семейство типов. Если бы я сделал что-то, в котором участвовали словари-словари вместо этого:
badEntailment :: Proxy a -> (Show a) :- (Ord a)
badEntailment _ = unsafeCoerceConstraint
Я предполагаю, что это почти наверняка будет способно вызвать segfault. Это правда? и если да, то чем он отличается от оригинала?
--- ИЗМЕНИТЬ 2 ---
Я просто хотел немного рассказать, почему меня это интересует. Одним из моих интересов является использование удобной кодировки реляционной алгебры в Haskell. Я думаю, что независимо от того, как вы определяете функции для работы над списками на уровне типов, будут очевидные вещи, которые не были бы корректно доказаны. Например, ограничение (для semijoin), которое у меня было раньше, выглядело так (это из памяти, поэтому это может быть не так):
semijoin :: ( GetOverlap as bs ~ Overlap inAs inBoth inBs
, HasElem x as, HasElem x (inAs ++ inBoth ++ inBs)) => ...
Итак, должно быть очевидно (человеку), что если я возьму объединение двух наборов, он содержит элемент x
, который был в as
, но я не уверен, что это возможно, чтобы законно убедить решатель ограничений этого. Итак, это моя мотивация для этого. Я создаю вложения, чтобы обмануть решателя ограничений, но я не знаю, действительно ли это безопасно.