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

11

Я хочу определить класс типов для геометрических объектов, которые могут пересекаться вместе:

class Intersect a b c | a b -> c where
  intersect :: a -> b -> c
-- Language extensions: -XMultiParamTypeClasses, -XFunctionalDependencies

Идея состоит в том, чтобы иметь функции пересечения общего назначения, которые могут обрабатывать объекты разных типов. Можно представить такие случаи, как

instance Intersect Line Plane (Maybe Point) where
  ...
instance Intersect Plane Plane (Maybe Line) where
  ...

Но я также хочу объявить, что пересечение коммутативно:

instance (Intersect a b c) => Intersect b a c where
  intersect x y = intersect y x
-- Language extensions: -XUndecidableInstances

Проблема в том, что всякий раз, когда я оцениваю intersect x yбез предварительного определения экземпляра формы Intersect a b c, где aтип xи bтип y, программа входит в бесконечный цикл , предположительно вызванный рекурсивным объявлением экземпляра о коммутативности. В идеале я хочу, чтобы что-то вроде intersect Egg Baconнеудачной проверки типов, потому что ни один такой экземпляр не был определен, не заманивает меня в бесконечный цикл. Как я могу это реализовать?

Хернг Йи
источник
Похоже, что-то, что вы можете попытаться сделать, используя семейства типов. Вы можете получить лучший ответ на переполнение стека.
Бенджамин Ходжсон
2
Вот запись в блоге о монаде, которая навязывает коммутативность, может быть, она может помочь: gelisam.blogspot.ca/2013/07/the-commulative-monad.html
Даниэль Диас Кэррет

Ответы:

2

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

instersect :: Commutative a b -> c

Тем не менее, вы также можете использовать QuickCheck с hspec для запуска теста свойств во всех экземплярах вашего класса типов, чтобы убедиться, что он действительно коммутирует. Это может снизить накладные расходы - вам нужно будет сделать тест, так как я не знаю, по макушке. Например:

import Test.Hspec

main :: IO ()
main = hspec $ do
    describe "intersect" $ do
        parallel $ it "should commute" $ do
            property $ \x y -> intersect x y == intersect (y :: Point) (x :: Line)

источник