“模式匹配”代数类型数据构造函数 [英] "Pattern matching" of algebraic type data constructors

查看:154
本文介绍了“模式匹配”代数类型数据构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

 数据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 Showable (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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆