Verwenden von Haskell-Typklassen zum Erzwingen der Kommutativität

11

Ich möchte eine Typklasse für geometrische Objekte definieren, die zusammen geschnitten werden können:

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

Die Idee ist, universelle Schnittfunktionen zu haben, die Objekte unterschiedlichen Typs verarbeiten können. Man könnte sich solche Fälle vorstellen wie

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

Ich möchte aber auch erklären, dass die Kreuzung kommutativ ist:

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

Das Problem ist, dass jedes Mal, wenn ich auswerte, intersect x yohne zuerst eine Instanz des Formulars zu definieren Intersect a b c, wobei aes sich um den Typ xund bden Typ handelt y, das Programm in eine Endlosschleife gerät , die vermutlich durch eine rekursive Instanzdeklaration über die Kommutativität verursacht wird. Im Idealfall möchte ich, dass intersect Egg Bacondie Typprüfung fehlschlägt, weil keine solche Instanz definiert wurde, und mich nicht in einer Endlosschleife einschließt. Wie kann ich das umsetzen?

Herng Yi
quelle
Klingt nach etwas, das Sie mit Typfamilien versuchen könnten. Möglicherweise erhalten Sie eine bessere Antwort auf den Stapelüberlauf.
Benjamin Hodgson
2
Hier ist ein Blog-Beitrag über eine Monade, die die Kommutativität erzwingt. Vielleicht kann sie helfen: gelisam.blogspot.ca/2013/07/the-commutative-monad.html
Daniel Díaz Carrete

Antworten:

2

Erstens könnten Sie das kommutative Paket verwenden. In diesem Fall würden Sie die Typensignatur intersectwie folgt ändern , andernfalls würde der Rest Ihres Codes "nur funktionieren":

instersect :: Commutative a b -> c

Sie können QuickCheck jedoch auch mit hspec verwenden, um einen Eigenschaftstest für alle Instanzen Ihrer Typklasse auszuführen, um sicherzustellen, dass diese tatsächlich pendelt. Dies kann den Overhead reduzieren - Sie müssten einen Benchmark durchführen, da ich es nicht genau weiß. Zum Beispiel:

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)

quelle