Checking that two values ​​have the same main constructor

I would like to write a function that checks that two values ​​were constructed using the same head constructor. This function:

  • should not be linear in the size of the data type declaration

  • should continue to work if the data type is extended.

eg. this is not satisfactory (it is linear, and catchall will invalidate the function if I add an extra constructor):

data E = A Int | B String | C

sameCons :: E -> E -> Bool
sameCons t u = case (t, u) of
  (A{}, A{}) -> True
  (B{}, B{}) -> True
  (C{}, C{}) -> True
  _          -> False

In OCaml, you can use unsafe functions from a module Objto do just that. Can we do something similar in Haskell (a ghc specific solution also works)?

+6
source share
2 answers

Data, .

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Data

data E = A Int | B String | C deriving (Typeable, Data)

sameCons :: E -> E -> Bool
sameCons x y = toConstr x == toConstr y

ghci> sameCons (A 1) (A 3)
True
ghci> sameCons (A 1) (C)
False
+6

GHC.Generics, , Orgazoid.

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeOperators #-}

import Data.Function (on)
import GHC.Generics

class GSameCons f where
  gSameCons :: f p -> f p -> Bool

instance GSameCons f => GSameCons (D1 c f) where
  gSameCons (M1 a) (M1 b) = gSameCons a b

instance (GSameCons f, GSameCons g) => GSameCons (f :+: g) where
  gSameCons (L1 a) (L1 b) = gSameCons a b
  gSameCons (R1 a) (R1 b) = gSameCons a b
  gSameCons _ _ = False

instance GSameCons (C1 c f) where
  gSameCons _ _ = True

data E = A Int | B String | C deriving Generic

sameCons :: (GSameCons (Rep a), Generic a) => a -> a -> Bool
sameCons = gSameCons `on` from

main = do
  print (sameCons (A 1) (A 2))
  print (sameCons (B "") C)
+3

Source: https://habr.com/ru/post/1017220/


All Articles