Search code examples
haskellpattern-matchingoperatorscommutativity

Commutative Property for Haskell Operators?


Is there a way to state that an operator is commutative, so that I don't have to give identical definitions for both directions? For instance:

data Nat = Zero | Succ Nat

(+) :: Nat -> Nat -> Nat
Zero + x = x
x + Zero = x
...

Here, is there a way such that I wouldn't have to give both of those definitions, that one of them would be implied from the other? Is there some way to state that fn = flip fn?


Solution

  • It’s not necessary for this addition operator, but in general you can make a function commutative without implementing all the flipped cases by adding a final equation that flips the arguments:

    data X = A | B | C
    
    adjacent A B = True
    adjacent B C = True
    adjacent A C = False
    adjacent x y = adjacent y x  -- covers B A, C B, and C A
    

    However, the downside is that if you forget to handle a case, this easily leads to an infinite loop:

    adjacent A B = True
    adjacent B C = True
    adjacent x y = adjacent y x
    

    Here, adjacent A C would call adjacent C A, which would call adjacent A C, and so on. And GHC’s pattern match exhaustivity checking (-fwarn-incomplete-patterns or -Wall) won’t help you here.

    I guess you could add an additional argument to prevent looping:

    data Commute = Forward | Reverse
    
    adjacent = go Forward
      where
        go _ A B = True
        go _ B C = True
        go Forward x y = go Reverse y x  -- try to commute
        go Reverse _ _ = False           -- commuting failed
    

    Now GHC will complain if you don’t add the go Reverse equation to handle the case where you commuted but there was still no match.

    But I think this is only suitable for functions with a large number of cases—otherwise, it’s much clearer to simply enumerate them all.