{-# LANGUAGE GADTs                #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE UndecidableInstances #-}

{-|

Module      : Expr
Description : Simple evaluator for lambda calculus expressions.
Copyright   : © Frank Jung, 2023
License     : GPL-3

Generalized Algebraic Datatypes example from
<https://link.springer.com/book/10.1007/978-3-540-76786-2 Datatype-Generic Programming>.

-}

module Expr (Expr(..), eval) where

data Expr a where
    Num :: Int -> Expr Int
    Plus :: Expr Int -> Expr Int -> Expr Int
    Eq :: Expr Int -> Expr Int -> Expr Bool
    If :: Expr Bool -> Expr e -> Expr e -> Expr e

eval :: Expr e -> e
eval :: forall e. Expr e -> e
eval (Num Int
n)       = e
Int
n
eval (Plus Expr Int
e1 Expr Int
e2)  = Expr e -> e
forall e. Expr e -> e
eval Expr e
Expr Int
e1 e -> e -> e
forall a. Num a => a -> a -> a
+ Expr e -> e
forall e. Expr e -> e
eval Expr e
Expr Int
e2
eval (Eq Expr Int
e1 Expr Int
e2)    = Expr Int -> Int
forall e. Expr e -> e
eval Expr Int
e1 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Expr Int -> Int
forall e. Expr e -> e
eval Expr Int
e2
eval (If Expr Bool
e1 Expr e
e2 Expr e
e3) = if Expr Bool -> Bool
forall e. Expr e -> e
eval Expr Bool
e1 then Expr e -> e
forall e. Expr e -> e
eval Expr e
e2 else Expr e -> e
forall e. Expr e -> e
eval Expr e
e3