我希望能够编写一个函数来检查是否使用相同的头构造函数构建了两个值。这个功能:
不应该是数据类型声明大小的线性
应继续工作
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特定的解决方案也适用)吗?
答案 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)
我们最终还是需要一个构造函数名称的数据类型,因此这是我们当前的解决方案,它不依赖于Data
或GHC.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