-- * 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