在 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)
这看起来有点过于复杂了。我不需要 Sign
和 Dir
是不同的类型,我只想将事物与它们的构造函数进行模式匹配。那么有没有更好的方法来定义这种类型?
所以我想做的是某种
data Direction = Direction (DirX | DirY | DirZ | DirW) (Pos | Neg) deriving (Show, Eq)
因为我做 \(Direction _ s) -> if s == Pos then ... else ...
之类的事情,但我从不在 Direction
之外使用 Dir
或 Sign
我想我不是第一个问这个问题的人,但我真的不知道如何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 等同于 type
。 Bool
是 Sign
的现成替代品,尽管 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"
不过,最后,我不认为将 Sign
和 Dir
定义为不同的类型是个问题。
我有一个表示方向的类型,它有一个轴和一个方向:
data Dir = DirX | DirY | DirZ | DirW deriving (Show, Eq)
data Sign = Pos | Neg deriving (Show, Eq)
newtype Direction = Direction (Dir, Sign) deriving (Show, Eq)
这看起来有点过于复杂了。我不需要 Sign
和 Dir
是不同的类型,我只想将事物与它们的构造函数进行模式匹配。那么有没有更好的方法来定义这种类型?
所以我想做的是某种
data Direction = Direction (DirX | DirY | DirZ | DirW) (Pos | Neg) deriving (Show, Eq)
因为我做 \(Direction _ s) -> if s == Pos then ... else ...
之类的事情,但我从不在 Direction
Dir
或 Sign
我想我不是第一个问这个问题的人,但我真的不知道如何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 等同于 type
。 Bool
是 Sign
的现成替代品,尽管 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"
不过,最后,我不认为将 Sign
和 Dir
定义为不同的类型是个问题。