检查两个值是否具有相同的头构造函数

时间:2017-05-12 18:10:12

标签: haskell

我希望能够编写一个函数来检查是否使用相同的头构造函数构建了两个值。这个功能:

  • 不应该是数据类型声明大小的线性

  • 如果数据类型已扩展,
  • 应继续工作

e.g。这是不满意的(它是线性的,如果我添加任何额外的构造函数,catchall将使函数无效):

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

在OCaml it is possible中使用Obj模块中的不安全函数来完成该操作。我们可以在Haskell中做类似的事情(ghc特定的解决方案也适用)吗?

3 个答案:

答案 0 :(得分:6)

如果你愿意推导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

答案 1 :(得分:3)

您也可以使用GHC.Generics执行此操作,但它比The 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)

答案 2 :(得分:0)

我们最终还是需要一个构造函数名称的数据类型,因此这是我们当前的解决方案,它不依赖于DataGHC.Generics

data E = A Int | B String | C
data EName = A_ | B_ | C_ deriving (Eq)

eName :: E -> EName
eName e = case e of
  A{} -> A_
  B{} -> B_
  C{} -> C_

sameCons :: E -> E -> Bool
sameCons = (==) `on` eName
相关问题