在 Haskell 中键入两个枚举字段

Type with two enum fields in Haskell

我有一个表示方向的类型,它有一个轴和一个方向:

data Dir = DirX | DirY | DirZ | DirW deriving (Show, Eq)
data Sign = Pos | Neg deriving (Show, Eq)
newtype Direction = Direction (Dir, Sign) deriving (Show, Eq)

这看起来有点过于复杂了。我不需要 SignDir 是不同的类型,我只想将事物与它们的构造函数进行模式匹配。那么有没有更好的方法来定义这种类型?

所以我想做的是某种

data Direction = Direction (DirX | DirY | DirZ | DirW) (Pos | Neg) deriving (Show, Eq)

因为我做 \(Direction _ s) -> if s == Pos then ... else ... 之类的事情,但我从不在 Direction

之外使用 DirSign

我想我不是第一个问这个问题的人,但我真的不知道如何google这个问题。

不,没有更好的方法了。

...话又说回来,就我个人而言,我已经觉得这种方式非常好。所以也许背负的担子并不是那么可怕。

当然,有一些方法可以定义您可能仍然感兴趣的完全不同的类型。例如,我过去使用了沿着这些方向的方向类型,效果很好:

data Direction = Direction { x, y, z, w :: Int }
aboutFace (Direction x y z w) = Direction (-x) (-y) (-z) (-w)
-- e is a traditional name from linear algebra for an axis-aligned unit vector
ex = Direction 1 0 0 0
ey = Direction 0 1 0 0
ez = Direction 0 0 1 0
ew = Direction 0 0 0 1

您可以使用模式同义词来匹配同构类型的值。将 pattern 视为 term-level 等同于 typeBoolSign 的现成替代品,尽管 Dir 没有方便的 4 路求和类型;您可以改用 Either Bool Bool

{-# LANGUAGE PatternSynonyms #-}

-- type Sign = Bool
-- All two-value types are isomorphic to Bool.
pattern Pos = True
pattern Neg = False

-- type Dir = Either Bool Bool
-- All four-value types are isomorphic to Either Bool Bool
pattern DirX = Left True
pattern DirY = Left False
pattern DirZ = Right True
pattern DirW = Right False

data Direction = Direction Bool (Either Bool Bool)

实际映射并不重要。例如,如果将 Pos 映射到 False 并将 Neg 映射到 True,这同样有效。重要的是映射是单射的。

您可以使用模式来创造价值

x = Direction Pos DirX

和模式匹配

showDir :: Direction -> String
showDir (Direction Pos DirX) = "+X"
-- etc

然后

> showDir x
"+X"

不过,最后,我不认为将 SignDir 定义为不同的类型是个问题。