-----------------------------                                             -- 001
-- Simple Functional Language                                             -- 002
-----------------------------                                             -- 003
                                                                          -- 004
                                                                          -- 005
-- variables are just names                                               -- 006
type Var = String                                                         -- 007
                                                                          -- 008
-- values are integers and functions                                      -- 009
data Value = ValInt Integer                                               -- 010
           | ValFunc Var Exp Env                                          -- 011
           | ValError String                                              -- 012
                                                                          -- 013
                                                                          -- 014
-- an Environment maps variables to Values                                -- 015
type Env = Var -> Value                                                   -- 016
                                                                          -- 017
                                                                          -- 018
-- auxiliary function to map Values to Booleans                           -- 019
isTrue :: Value -> Bool                                                   -- 020
isTrue (ValInt i) = (i /= 0)                                              -- 021
                                                                          -- 022
                                                                          -- 023
-- An empty Environment                                                   -- 024
emptyEnv :: Env                                                           -- 025
emptyEnv v = ValError ("undefined variable " ++ v)                        -- 026
                                                                          -- 027
                                                                          -- 028
-- bind a new value in an environment                                     -- 029
bind :: Var -> Value -> Env -> Env                                        -- 030
bind var val env = \v -> if var == v then val else env v                  -- 031
                                                                          -- 032
                                                                          -- 033
-- executes a binary operation on values                                  -- 034
binOp :: (Integer -> Integer -> Integer) -> Value -> Value -> Value       -- 035
binOp op (ValInt i1) (ValInt i2) = ValInt (op i1 i2)                      -- 036
binOp _ _ _ = ValError "binary operand is not a number"                   -- 037
                                                                          -- 038
                                                                          -- 039
fix :: (a -> a) -> a                                                      -- 040
fix f = x where x = f x                                                   -- 041
                                                                          -- 042
--------------------------------------------------------------------      -- 043
-- Abstract Syntax Tree for Expressions                                   -- 044
data Exp = ExpK Integer          -- constants                             -- 045
         | ExpVar Var            -- variables                             -- 046
         | ExpAdd Exp Exp        -- e1 + e2                               -- 047
         | ExpSub Exp Exp        -- e1 - e2                               -- 048
         | ExpMul Exp Exp        -- e1 * e2                               -- 049
         | ExpDiv Exp Exp        -- e1 / e2                               -- 050
         | ExpIf Exp Exp Exp     -- if e1 then e2 else e3                 -- 051
         | ExpApp Exp Exp        -- e1 e2                                 -- 052
         | ExpLambda Var Exp     -- \x -> e                               -- 053
         | ExpLet Var Exp Exp    -- letrec x=e1 in e2                     -- 054
                                                                          -- 055
-- Evaluates an expression in a given environment                         -- 056
evalExp :: Exp -> Env -> Value                                            -- 057
                                                                          -- 058
evalExp (ExpK i) env = ValInt i                                           -- 059
evalExp (ExpVar v) env = env v                                            -- 060
evalExp (ExpAdd e1 e2) env = binOp (+) (evalExp e1 env) (evalExp e2 env)  -- 061
evalExp (ExpSub e1 e2) env = binOp (-) (evalExp e1 env) (evalExp e2 env)  -- 062
evalExp (ExpMul e1 e2) env = binOp (*) (evalExp e1 env) (evalExp e2 env)  -- 063
evalExp (ExpDiv e1 e2) env = binOp div (evalExp e1 env) (evalExp e2 env)  -- 064
evalExp (ExpIf e1 e2 e3) env =                                            -- 065
    if isTrue(evalExp e1 env) then evalExp e2 env else evalExp e3 env     -- 066
evalExp (ExpApp e1 e2) env =                                              -- 067
  case (evalExp e1 env) of                                                -- 068
    ValFunc v b env' -> evalExp b (bind v (evalExp e2 env) env')          -- 069
    _         -> ValError "calling a non-function value"                  -- 070
evalExp (ExpLambda v e) env = ValFunc v e env                             -- 071
                                                                          -- 072
evalExp (ExpLet v e1 e2) env = evalExp e2 env'                            -- 073
  where env' = bind v (evalExp e1 env') env                               -- 074
                                                                          -- 075
                                                                          -- 076
---------------------------------------------------------------------------- 077
-------------------------------------------------------------------       -- 078
-- some examples                                                          -- 079
                                                                          -- 080
-- letrec f = \x -> if x then x * f(x - 1) else 1                         -- 081
-- in f 10                                                                -- 082
fat =                                                                     -- 083
  ExpLet "f"                                                              -- 084
    (ExpLambda "x"                                                        -- 085
       (ExpIf (ExpVar "x")                                                -- 086
              (ExpMul (ExpVar "x")                                        -- 087
                      (ExpApp (ExpVar "f") (ExpSub (ExpVar "x") (ExpK 1))))-- 088
              (ExpK 1)))                                                  -- 089
    (ExpApp (ExpVar "f") (ExpK 10))                                       -- 090
                                                                          -- 091
                                                                          -- 092
-- y = \f -> (\x -> f (x x)) (\x -> f (x x))                              -- 093
y = ExpLambda "f" (ExpApp y' y')                                          -- 094
  where y' = ExpLambda "x"                                                -- 095
               (ExpApp (ExpVar "f") (ExpApp (ExpVar "x") (ExpVar "x")))   -- 096
                                                                          -- 097
-- fatG = \f -> \x -> if x then x * f(x - 1) else 1                       -- 098
fatG =                                                                    -- 099
  ExpLambda "f"                                                           -- 100
    (ExpLambda "x"                                                        -- 101
       (ExpIf (ExpVar "x")                                                -- 102
              (ExpMul (ExpVar "x")                                        -- 103
                      (ExpApp (ExpVar "f") (ExpSub (ExpVar "x") (ExpK 1))))-- 104
              (ExpK 1)))                                                  -- 105
                                                                          -- 106
-- fat' = (y fatG) 20                                                     -- 107
fat' = ExpApp (ExpApp y fatG) (ExpK 20)                                   -- 108
                                                                          -- 109
-- code to show the final value of an expression                          -- 110
main :: IO ()                                                             -- 111
main = ((case (evalExp fat' emptyEnv) of                                  -- 112
                         ValInt i -> print i                              -- 113
                         ValFunc _ _ _ -> print "function"                -- 114
                         ValError err -> print ("error: " ++ err)))       -- 115
                                                                          -- 116