我正在用 Haskell 编写一个 Magic The Gathering (MTG) 游戏引擎。
对于那些不熟悉 MTG 的人来说,这是一款纸牌游戏,其中纸牌最多可以有 5 种颜色:白色 (W)、蓝色 (U)、黑色 (B)、红色 (R) 和绿色 (G)。
{-# LANGUAGE ViewPatterns #-}
import Data.Set
data Color = W | U | B | R | G
deriving (Show, Eq, Ord)
data Card = Card (Set Color) -- simplified Card type with only its colors
viewColors :: Card -> [Color]
viewColors (Card colors) = toList colors
我想做的是对颜色进行模式匹配,如下所示:
foo :: Card -> String
foo (viewColors -> [W, B]) = "card is white and black"
foo _ = "whatever"
到现在为止还挺好。但是这里有一个问题:我可以在视图模式中错误地键入颜色顺序,如下所示:
bar :: Card -> String
bar (viewColors -> [B, W]) = "this will never get hit"
bar _ = "whatever"
当然,我可以用viewColors
一种直接解决这个问题的方式来写。或者我可以使用警卫,但我不愿意。这里有几种方法可以做到这一点
viewColors :: Card -> (Bool, Bool, Bool, Bool, Bool)
viewColors (Card colors) = let m = (`member` colors)
in (m W, m U, m B, m R, m G)
这个解决方案在模式匹配时过于冗长,即使我使用同构Bool
但具有更短(和/或有意义)标识符的类型。匹配绿卡看起来像
baz :: Card -> String
baz (viewColors -> (False, False, False, False, True)) = "it's green"
data ColorView = W | WU | WUB | ... all combos here
viewColors :: Card -> ColorView
viewColors (Card colors) = extract correct Colorview from colors
该解决方案具有组合爆炸性。实现起来似乎非常糟糕,但使用起来很好,特别是如果我有一个colorViewToList :: ColorView -> [Color]
允许在模式匹配后进行编程提取。
我不知道是否可以在 Haskell 中近似以下内容,但以下内容将是理想的:
fuz :: Card -> String
fuz (viewColors -> (W :* ())) = "it's white"
fuz (viewColors -> (W :* U :* ())) = "it's white and blue"
fuz (viewColors -> (W :* B :* ())) = "it's white and black"
我愿意使用高级语言扩展来允许这种代码:DataKinds、PolyKinds、TypeFamilies、MultiParamTypeClasses、GADTs,应有尽有。
这样的事情可能吗?你有其他建议的方法吗?