Usar clases de tipo Haskell para imponer la conmutatividad

11

Quiero definir una clase de tipo para objetos geométricos que se puedan intersectar juntos:

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

La idea es tener funciones de intersección de propósito general que puedan manejar objetos de diferentes tipos. Uno podría imaginarse instancias como

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

Pero también quiero declarar que la intersección es conmutativa:

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

El problema es que cada vez que evalúo intersect x ysin definir primero una instancia de la forma Intersect a b c, donde aes el tipo de xy bes el tipo de y, el programa entra en un bucle infinito , presumiblemente causado por una declaración de instancia recursiva sobre la conmutatividad. Idealmente, quiero que algo como intersect Egg Baconno verificar el tipo porque no se definió tal instancia, no atraparme en un bucle infinito. ¿Cómo puedo implementar esto?

Herng Yi
fuente
Suena como algo que podrías intentar hacer usando familias de tipos. Es posible que obtenga una mejor respuesta en el desbordamiento de pila.
Benjamin Hodgson el
2
Aquí hay una publicación de blog sobre una mónada que aplica la conmutatividad, tal vez pueda ayudar: gelisam.blogspot.ca/2013/07/the-commutative-monad.html
Daniel Díaz Carrete

Respuestas:

2

Primero, puede usar el paquete conmutativo , en cuyo caso modificaría la firma de tipo de intersectlo siguiente, pero de lo contrario el resto de su código "simplemente funcionaría":

instersect :: Commutative a b -> c

Sin embargo, también puede usar QuickCheck con hspec para ejecutar una prueba de propiedad en todas las instancias de su clase de tipos para asegurarse de que realmente viaje. Esto puede reducir los gastos generales: tendrías que hacer un punto de referencia ya que no lo sé. Por ejemplo:

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)

fuente