I'm writing a Magic The Gathering (MTG) game engine in Haskell.
For those unfamiliar with MTG, it's a card game where cards can have up to 5 colors: White (W), Blue (U), Black (B), Red (R), and Green (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
What I would like to do is pattern match on colors like so:
foo :: Card -> String
foo (viewColors -> [W, B]) = "card is white and black"
foo _ = "whatever"
So far, so good. But there is one problem here: I can type the order of colors incorrectly in the view pattern like so:
bar :: Card -> String
bar (viewColors -> [B, W]) = "this will never get hit"
bar _ = "whatever"
Of course, I could have written viewColors
in a way that directly resolves this problem. Or I could use guards, but I'd rather not. Here are a couple ways to do so
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)
This solution is overly verbose while pattern matching, even if I use a type isomorphic to Bool
but with shorter (and/or meaningful) identifiers. Matching a Green card would look like
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
This solution has combinatorial explosion. Seems extremely bad to implement, but nice to use, especially if I have a colorViewToList :: ColorView -> [Color]
to allow programmatic extraction after the pattern match.
I have no idea if the following can be approximated in Haskell, but the following would be ideal:
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"
I'm willing to use advanced language extensions to allow this kind of code: DataKinds, PolyKinds, TypeFamilies, MultiParamTypeClasses, GADTs, you name it.
Is something like this possible? Do you have other suggested approaches?
f card | card
color` [B, W] = ... | cardcolor
[B,U,W] = ...`. Also, this sounds like a cool project; what are you planning to do with it? – Tikhon JelvisOverloadedLists
would help here. – user824425