Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking that two values have the same head constructor

Tags:

haskell

I'd like to be able to write a function which checks that two values have been built using the same head constructor. This function:

  • shouldn't be linear in the size of the datatype declaration

  • should keep working if the datatype is extended

e.g. this is not satisfactory (it is linear and the catchall will invalidate the function if I add any 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 it is possible to use unsafe functions from the Obj module to do exactly that. Can we do something similar in Haskell (a ghc-specific solution works too)?

like image 401
gallais Avatar asked May 12 '17 18:05

gallais


2 Answers

If you're willing to derive Data then you're good to go.

{-# 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
like image 93
Benjamin Hodgson Avatar answered Oct 06 '22 07:10

Benjamin Hodgson


You can also do this with GHC.Generics, but it's more boilerplate than The Orgazoid's answer.

{-# 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)
like image 24
Li-yao Xia Avatar answered Oct 06 '22 06:10

Li-yao Xia