I have this recursive, algebraic data type for representing colors and mixing/inverting particular RGB channels:
data Color = Red | Green | Blue | Mix Color Color | Invert Color
deriving Show
rgb :: Color -> [Double]
rgb Red = [1,0,0]
rgb Green = [0,1,0]
rgb Blue = [0,0,1]
rgb (Invert Red) = let cols = rgb Red
in [1 - cols !! 0, cols !! 1, cols !! 2]
rgb (Invert Green) = let cols = rgb Red
in [cols !! 0, 1 - cols !! 1, cols !! 2]
rgb (Invert Blue) = let cols = rgb Red
in [cols !! 0, cols !! 1, 1 - cols !! 2]
rgb (Invert color) = rgb color
rgb (Mix Red Green) = let cols1 = rgb Red
cols2 = rgb Green
cols3 = rgb Blue
ave = (cols1 !! 0 + cols2 !! 1) / 2
in [ave, ave, cols3 !! 2]
rgb (Mix Green Red) = rgb (Mix Red Green)
rgb (Mix Green Blue) = let cols1 = rgb Red
cols2 = rgb Green
cols3 = rgb Blue
ave = (cols1 !! 0 + cols2 !! 1) / 2
in [ave, ave, cols3 !! 2]
rgb (Mix Blue Green) = rgb (Mix Green Blue)
rgb (Mix Red Blue) = let cols1 = rgb Red
cols2 = rgb Green
cols3 = rgb Blue
ave = (cols1 !! 0 + cols2 !! 1) / 2
in [ave, ave, cols3 !! 2]
rgb (Mix Blue Red) = rgb (Mix Red Blue)
When I run the code in
main :: IO ()
main = do
print (rgb (Mix Red Green))
print (rgb (Mix Red Green))
print (rgb (Mix Red (Mix Red Green)))
print (rgb (Invert Red))
print (rgb (Invert (Mix Red (Mix Red Green))))
print (rgb (Mix (Invert Red) (Invert Green)))
I get the runtime exception:
[1.0,1.0,1.0]
[1.0,1.0,1.0]
*** Exception: Main.hs:(158,1)-(195,39): Non-exhaustive patterns in function rgb
What am I doing wrong here?
(I just started with Haskell algebraic datatypes, so, of course, I make mistakes.)
For Mix
, you only pattern match on Mix Red Green
, Mix Blue Red
, etc. Not on Mix (Invert Red) Green
, etc. or Mix (Mix Red Green) Red
. Making patterns for all these is impossible, since your data structure can produce an infinite amount of values. Indeed, for example rgb (Invert (Invert (… (Invert Red) …)))
with an arbitrary number of Invert
s.
You can pick the value(s) wrapped in Invert
or Mix
and then call rgb
on these and invert or mix the end product, like:
rgb :: Color -> [Double]
rgb Red = [1, 0, 0]
rgb Green = [0, 1, 0]
rgb Blue = [0, 0, 1]
rgb (Invert x) = map (1 -) (rgb x)
rgb (Mix x1 x2) = zipWith mid (rgb x1) (rgb x2)
where
mid y1 y2 = (y1 + y2) / 2
For the sample data, this then produces:
ghci> rgb (Mix Red Green)
[0.5,0.5,0.0]
ghci> rgb (Mix Red Green)
[0.5,0.5,0.0]
ghci> rgb (Mix Red (Mix Red Green))
[0.75,0.25,0.0]
ghci> rgb (Invert Red)
[0.0,1.0,1.0]
ghci> rgb (Invert (Mix Red (Mix Red Green)))
[0.25,0.75,1.0]
ghci> rgb (Mix (Invert Red) (Invert Green))
[0.5,0.5,1.0]