-- * Tagless Typed Interpreters, introduction

module Intro1 where

-- We would like to embed an (object) language whose expressions
-- look like
--     -(1 + 2)
-- *     8 + (- (1+2))
-- The expressions are built of integer literals, negation and addition.
-- The latter is one of our running example, please take note.

-- * Deep/initial embedding
-- The data type of expressions
data Exp = Lit Int 
	 | Neg Exp
	 | Add Exp Exp

-- A sample expression: our first running example
ti1 = Add (Lit 8) (Neg (Add (Lit 1) (Lit 2)))

-- * //
-- The evaluator
-- It proceeds by case analysis on the expression
eval:: Exp -> Int
eval (Lit n)     = n
eval (Neg e)     = - eval e
eval (Add e1 e2) = eval e1 + eval e2


ti1_eval = eval ti1
-- 5

-- * //
-- A different way to write the evaluator:
-- using the `constructor functions'

type Repr = Int

lit :: Int -> Repr
lit n = n

neg :: Repr -> Repr
neg e = - e

-- What is the inferred type for add? What type should we rather assign?
add e1 e2 = e1 + e2

-- Perhaps this already reminds someone of the denotational semantics.
-- More reminders to come.


-- Our sample expression in the final form:
-- I just downcased ti1 
tf1 = add (lit 8) (neg (add (lit 1) (lit 2)))
-- 5

-- * Shallow (metacircular) embedding (to be called final)
-- * //
-- But the datatype way (initial way) lets us `evaluate' ti1
-- in a different way: to pretty-print it

view:: Exp -> String
view (Lit n) = show n
view (Neg e) = "(-" ++ view e ++ ")"
view (Add e1 e2) = "(" ++ view e1 ++ " + " ++ view e2 ++ ")"

ti1_view = view ti1
-- "(8 + (-(1 + 2)))"

-- How can we evaluate tf1 differently? It seems that the evaluator
-- is hard-wired into tf1...
-- Indeed, the constructor functions lit, neg, and add all have
-- return type that is set to Int. The evaluator `view' returns a string.
-- So, at the very least we must find a way to parameterize the constructor
-- functions by the result type.


main = do
       print ti1_eval
       print tf1
       print ti1_view
