title: Haskell - Pattern matching with data types
body: <p>I have a data type and function like this: </p>
  
  <pre><code>data Expr = Num Int | Add Expr Expr | Mult Expr Expr | Neg Expr | If Expr Expr Expr deriving (Show, Read)
  
  prettyPrint :: Expr -&gt; IO () 
  prettyPrint expr = prettyPrint' expr 0
  
  
  prettyPrint' :: Expr -&gt; Int -&gt; IO () 
  prettyPrint' (Num x) i = putStrLn $ concat (replicate i "    ") ++ "Num " ++ show x
  
  prettyPrint' (Add x y) i = do
      putStrLn $ concat (replicate i "    ") ++ "Add" 
      prettyPrint' x (i+1) 
      prettyPrint' y (i+1) 
  
  prettyPrint' (Mult x y) i = do
      putStrLn $ concat (replicate i "    ") ++ "Mult" 
      prettyPrint' x (i+1) 
      prettyPrint' y (i+1) 
  
  prettyPrint' (Neg x) i = do
      putStrLn $ concat (replicate i "    ") ++ "Neg" 
      prettyPrint' x (i+1) 
  
  prettyPrint' (If x y z) i = do 
      putStrLn $ concat (replicate i "    ") ++ "If" 
      prettyPrint' x (i+1) 
      prettyPrint' y (i+1) 
      prettyPrint' z (i+1) 
  </code></pre>
  
  <p>In the function I am using pattern matching. The problem is that their is a lot of reuse of code. For example, the case for <code>Mult</code> and <code>Add</code> is basically the same code. Same goes for <code>Num</code> and <code>Neg</code>. Is there a way to write this based on how many variables the expression have? Like one for <code>Num</code> and <code>Neg</code>, since they have only one variable. One case for <code>Mult</code> and <code>Add</code>, since they have two variables. And a last case for <code>If</code>, since that expression have three variables.</p>
  
  <p>NOTE: </p>
  
  <p>I landed on this answer, I think it's a better solution than I started with: </p>
  
  <pre><code>prettyPrint :: Expr -&gt; IO () 
  prettyPrint expr = putStrLn (prettyPrint' 1 expr)
  
  prettyPrint' :: Int -&gt; Expr -&gt; String
  prettyPrint' i (Num x) = "Num " ++ show x 
  prettyPrint' i expr = 
      let indent x = concat (replicate i "    ") ++ x 
          (op, args) = case expr of
              Add x y  -&gt; ("Add",  [x,y])
              Mult x y -&gt; ("Mult", [x,y])
              Neg x    -&gt; ("Neg",  [x])
              If x y z -&gt; ("If",   [x,y,z])
      in intercalate "\n" (op : map (indent . prettyPrint' (i + 1)) args)
  </code></pre>