Korzystanie z klas typu Haskell w celu wymuszenia komutatywności

11

Chcę zdefiniować klasę typów dla obiektów geometrycznych, które można przecinać razem:

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

Chodzi o to, aby mieć funkcje przecięcia ogólnego przeznaczenia, które mogą obsługiwać obiekty różnych typów. Można sobie wyobrazić takie przypadki jak

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

Ale chcę również zadeklarować, że skrzyżowanie jest przemienne:

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

Problem polega na tym, że ilekroć oceniam intersect x ybez uprzedniego zdefiniowania wystąpienia formy Intersect a b c, gdzie ajest typ xi bjest typ y, program przechodzi w nieskończoną pętlę , prawdopodobnie spowodowaną deklaracją instancji rekurencyjnej o komutatywności. Idealnie intersect Egg Baconbyłoby, gdyby coś takiego nie sprawdziło się podczas sprawdzania typu, ponieważ nie zdefiniowano takiej instancji, aby nie uwięzić mnie w nieskończonej pętli. Jak mogę to zaimplementować?

Herng Yi
źródło
Brzmi jak coś, co możesz spróbować zrobić przy użyciu rodzin typów. Możesz uzyskać lepszą odpowiedź na przepełnienie stosu.
Benjamin Hodgson,
2
Oto post na blogu o monadzie, która wymusza komutatywność
Daniel Díaz Carrete

Odpowiedzi:

2

Po pierwsze, możesz użyć pakietu przemiennego , w którym to przypadku zmodyfikujesz podpis typu intersectdo następującego, ale w przeciwnym razie reszta kodu „po prostu zadziała”:

instersect :: Commutative a b -> c

Jednak możesz także użyć QuickCheck z hspec, aby uruchomić test właściwości we wszystkich instancjach twojej klasy, aby upewnić się, że faktycznie dojeżdża do pracy. Może to zmniejszyć koszty ogólne - musisz zrobić punkt odniesienia, ponieważ nie wiem od samego początku. Na przykład:

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)

źródło