{-# LANGUAGE DeriveDataTypeable #-}

import Data.Generics
import Data.Set (Set)
import qualified Data.Set as Set

data Expr = Var Name | App Expr Expr | Lam Name Expr
  deriving (Show, Data, Typeable)

type Name = String

-- All the boring cases (just one in this example) can be taken
-- care of at once. But gmapQr etc. are complicated and often
-- inefficient.  See NoGenerics for a nicer way to do it.
freeVars :: Expr -> Set Name
freeVars (Var x)   = Set.singleton x
freeVars (Lam x e) = Set.delete x $ freeVars e
freeVars e         = gmapQr Set.union Set.empty 
                            (mkQ Set.empty freeVars) e

-- Examples
e1, e2, e3 :: Expr
e1 = Lam "x" $ Var "x"
e2 = Lam "unused" $ Var "C"
e3 = Lam "x" $ Lam "y" $ Lam "z" $ 
     App (Var "x") (Var "z") `App` 
     App (Var "y") (Var "z")