定义Haskell类型的构造函数是否相等?

时间:2013-04-15 03:54:11

标签: haskell algebraic-data-types pattern-synonyms

我不是作为Eq成员的意思。我的代码:

data Race = Terran | Zerg | Protoss deriving (Eq, Show, Read);

data MU = MU Race Race deriving (Eq, Show);

在这种情况下,我定义了例如(MU Terran Zerg)。我想创建一个数据构造函数TvZ,它在intance的各个方面基本相同,所以我可以匹配一个函数模式:

foo TvZ = ...

而不是必须

foo (MU Terran Zerg) = ...

如果您将其分配给tvZ = (MU Terran Zerg)

中的变量,则无法执行此操作

我想要做的另一件事是制作简短的表单,就像使类型构造函数TTerran相同。

最后一点,medivac的速度提升需要我感觉到的轻微神经。

3 个答案:

答案 0 :(得分:8)

您要求的内容称为“模式同义词”,并已多次提出。目前尚未实施。您可以查看提案herehere以及其他一些地方(链接由hammar提供)。

然而,作为一种解决方案,这也同样适用:

foo (MU Terran Zerg) = ...
foo (MU Zerg Terran) = foo $ MU Terran Zerg

并且会有效地实现同样的目标,即使它看起来不那么好。

答案 1 :(得分:6)

首先尝试使用以下模式(我假设允许MU Terran Terran和其他自我关系。):

foo (MU Terran Terran)   = ...
foo (MU Terran Zerg)     = ...
foo (MU Terran Protoss)  = ...
foo (MU Zerg Zerg)       = ...
foo (MU Zerg Protoss)    = ...
foo (MU Protoss Protoss) = ...
foo (MU x y) = foo (MU y x)

你必须非常小心这样定义的函数,因为如果你没有让案例详尽无遗,那就是无限循环。

第二次尝试:我试图概括模式,我想出的最好的就是这个,这几乎没有更好:

forceSymmetric :: (MU -> Maybe r) -> MU -> r
forceSymmetric f = \p -> case f p of
                          Nothing -> fromJust (f (swap p))
                          Just r -> r

foo (MU Terran Terran)   = Just ...
foo (MU Terran Zerg)     = Just ...
foo (MU Terran Protoss)  = Just ...
foo (MU Zerg Zerg)       = Just ...
foo (MU Zerg Protoss)    = Just ...
foo (MU Protoss Protoss) = Just ...
foo (MU x y)             = Nothing

如果你搞砸了,你就会产生错误,而不是无限循环。

第三,更深入的尝试:问题的核心是你想要对称。让我们忘记MU是一个构造函数,并将它视为一个函数。你希望它遵守这种对称性法则:

MU a b == MU b a

==我不一定是Eq类型,而是相互替代性;将一个表达式替换为另一个表达式不应影响任何程序的含义。

嗯,代数数据类型没有那个属性,句号。对于MUMU a b == MU c d等代数数据类型构造函数,当且仅当a == bc == d时。因此,如果您希望任何函数无法区分MU Terran ZergMU Zerg Terran,则需要使MU类型为抽象,以便其用户无法看到其内部表示。< / p>

一次 n 项目的 n 项目的组合公式,允许重复,为factorial (n + r - 1) / (factorial r * factorial (n - 1));对于n = 3r = 2,这是6种组合。所以我们想要的是定义一个只有六个可能值的类型MU,一个toMU :: Race -> Race -> MU函数mu a b == mu b a和一个fromMU :: MU -> (Race, Race)函数{{1} }。我能想到的最简单的方法是使用已排序的元组:

uncurry toMU . fromMU == id

现在您可以保证data Race = Terran | Zerg | Protoss deriving (Eq, Show, Read, Ord); data SortedPair a = SP a a -- The constructor here needs to be private makeSortedPair :: Ord a => a -> a -> SortedPair a makeSortedPair a b | a < b = SP a b | otherwise = SP b a breakSortedPair :: SortedPair a a -> (a, a) breakSortedPair (SP a b) = (a, b) type MU = SortedPair Race toMU :: Race -> Race -> MU toMU = makeSortedPair fromMU :: MU -> (Race, Race) fromMU = breakSortedPair 可以产生fromMU但不能产生(Terran, Zerg),因此您可以省略上述前两个提案中的最终“全能”案例。 (然而,编译器对此一无所知,因此它仍会抱怨非详尽的模式。)

答案 2 :(得分:0)

从GHC 7.8开始,您可以使用pattern synonyms,这是一个针对此用例的新语言扩展程序:

{-# LANGUAGE PatternSynonyms #-}

pattern TvZ :: MU
pattern TvZ = MU Terran Zerg

这将允许您在模式上下文(即匹配)和表达式上下文(即构造新的TvZ值)中使用MU