“模式匹配”代数类型数据构造函数 [英] "Pattern matching" of algebraic type data constructors
问题描述
数据T = Alpha Int | Beta Int | Gamma Int Int | Delta Int
我想编写一个函数来检查两个值是否使用相同的构造函数生成: / p>
sameK(Alpha _)(Alpha _)= True
sameK(Beta _)(Beta _)= True
sameK(Gamma _ _)(Gamma _)= True
sameK _ _ = False
维护 sameK
没有多少乐趣,因此无法轻易检查正确性。例如,当新的构造函数被添加到 T
时,很容易忘记更新 sameK
。我省略了一行举例:
- 很容易忘记:
- sameK(Delta _ )(Delta _)= True
问题是如何避免萨梅克
?或者如何确保它检查所有 T
构造函数?
我发现的解决方法是为每个构造函数使用不同的数据类型,派生 Data.Typeable
,并声明一个通用类型类,但是我没有就像这个解决方案一样,因为它的可读性要低得多,否则只是一个简单的代数类型适用于我:
{ - #LANGUAGE DeriveDataTypeable# - }
import Data.Typeable
class Tlike t where
value :: t - > t
value = id
数据Alpha = Alpha Int派生Typeable
数据Beta = Beta Int派生Typeable
数据Gamma = Gamma Int Int派生Typeable
数据Delta = Delta Int推导类型
实例Tlike Alpha
实例Tlike Beta
实例Tlike Gamma
实例Tlike Delta
sameK :: (Tlike t,Typeable t,Tlike t',Typeable t')=> t - > t' - > Bool
sameK ab = typeOf a == typeOf b
看看Data.Data模块,特别是 toConstr
函数。与 { - #LANGUAGE DeriveDataTypeable# - }
一起,将为您提供一种适用于任何类型(实例 Data)的单线解决方案。数据
。你不需要弄清楚所有的SYB!
如果由于某种原因(坚持拥抱?),这不是一个选项,那么这里是一个非常丑陋和非常缓慢的黑客。它只适用于你的数据类型显示
能够(例如通过使用派生(显示)
- 这意味着里面没有函数类型,例如)。
constrT :: T - >字符串
constrT = head。单词。 show
sameK xy = constrT x == constrT y
constrT
通过显示它来获取 T
值的最外层构造函数的字符串表示形式,将其切分成单词,然后获取第一个。我给出了一个明确的类型签名,所以你不会试图在其他类型上使用它(并避免单态的限制)。
一些显着的缺点: / strong>
- 当你的类型有中缀构造函数时(例如
data T2 = Eta Int | T2如果你的一些构造函数有一个共享的前缀,这将变得更慢,因为大部分的字符串必须是
- 不适用于具有自定义
show
的类型,如许多库类型。
- 不适用于具有自定义
也就是说,它是 Haskell 98 ...但这只是我能说的唯一的好东西!
Let's consider a data type with many constructors:
data T = Alpha Int | Beta Int | Gamma Int Int | Delta Int
I want to write a function to check if two values are produced with the same constructor:
sameK (Alpha _) (Alpha _) = True
sameK (Beta _) (Beta _) = True
sameK (Gamma _ _) (Gamma _ _) = True
sameK _ _ = False
Maintaining sameK
is not much fun, it cannot be checked for correctness easily. For example, when new constructors are added to T
, it's easy to forget to update sameK
. I omitted one line to give an example:
-- it’s easy to forget:
-- sameK (Delta _) (Delta _) = True
The question is how to avoid boilerplate in sameK
? Or how to make sure it checks for all T
constructors?
The workaround I found is to use separate data types for each of the constructors, deriving Data.Typeable
, and declaring a common type class, but I don't like this solution, because it is much less readable and otherwise just a simple algebraic type works for me:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Typeable
class Tlike t where
value :: t -> t
value = id
data Alpha = Alpha Int deriving Typeable
data Beta = Beta Int deriving Typeable
data Gamma = Gamma Int Int deriving Typeable
data Delta = Delta Int deriving Typeable
instance Tlike Alpha
instance Tlike Beta
instance Tlike Gamma
instance Tlike Delta
sameK :: (Tlike t, Typeable t, Tlike t', Typeable t') => t -> t' -> Bool
sameK a b = typeOf a == typeOf b
Look at the Data.Data module, the toConstr
function in particular. Along with {-# LANGUAGE DeriveDataTypeable #-}
that will get you a 1-line solution which works for any type which is an instance of Data.Data
. You don't need to figure out all of SYB!
If, for some reason (stuck with Hugs?), that is not an option, then here is a very ugly and very slow hack. It works only if your datatype is Show
able (e.g. by using deriving (Show)
- which means no function types inside, for example).
constrT :: T -> String
constrT = head . words . show
sameK x y = constrT x == constrT y
constrT
gets the string representation of the outermost constructor of a T
value by showing it, chopping it up into words and then getting the first. I give an explicit type signature so you're not tempted to use it on other types (and to evade the monomorphism restriction).
Some notable disadvantages:
- This breaks horribly when your type has infix constructors (such as
data T2 = Eta Int | T2 :^: T2
) - If some of your constructors have a shared prefix, this is going to get slower, as a larger part of the strings has to be compared.
- Doesn't work on types with a custom
show
, such as many library types.
That said, it is Haskell 98... but that's about the only nice thing I can say about it!
这篇关于“模式匹配”代数类型数据构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!