Oleg,

thank you so much for taking the time to provide the example code and the  
explanation, it really helps.

My mental typechecker seems to diverge on the  "data EQ a b where  Refl :: EQ 
a  a" bit so I am now reading the "typing dynamic typing" paper, hoping for 
further enlightment.


If you don't mind, I would have a question regarding the usage of Template 
Haskell to convert the AST to a typed term.

I have found this paper by Louis-Julien Guillemette and Stefan Monnier : 

http://www.iro.umontreal.ca/~monnier/tct.pdf

The code that I could extract from the paper (there doesn't seem to be a code 
distribution) follows.

I don't see however how this could possibly be used to implement an 
interpreter, certainly the AST must be known at compile time, or am I missing 
something?



Thanks again,

              titto


> {-# OPTIONS -fglasgow-exts -fth #-}
> module HOASEval where
> import Language.Haskell.TH

> type VarId = String

> data AST = Fvar VarId | Flam VarId AST | Fapp AST AST 

> lift :: AST  -> ExpQ
> lift (Fvar  x)   = varE (mkName x)
> lift (Flam  x b) = [| Lam $(lam1E (varP (mkName x)) (lift b)) |]
> lift (Fapp  a b) = [| App $(lift a) $(lift b) |]

> data Term t where  
>  Num  :: Int -> Term Int 
>  Prim :: PrimOp -> Term Int -> Term Int -> Term Int  
>  If0  :: Term Int -> Term t -> Term t -> Term t  
>  Lam  :: (Term s -> Term t) -> Term (s -> t)        
>  App  :: Term (s -> t) -> Term s -> Term t

> data PrimOp = Add | Sub | Mult   

> e0 = Flam "x" (Fvar "x") 

> eval ast = $(lift ast)

> test = eval e0



On Thursday 04 October 2007 07:02:32 [EMAIL PROTECTED] wrote:
> Pasqualino 'Titto' Assini wrote:
> > I am trying to write an interpreter for a little functional language but
> > I am finding very problematic to dynamically create a typed
> > representations of the language terms.
> >
> > > The problem is to write a function that converts between Exp and Term
> > >
> > > t as in:
> > > > test :: Term Double
> > > > test = typecheck $ EApp (EPrim "inc") (EDouble 10.0)
>
> The specification leaves out a few important details. The typechecker can't
> be a total function: what happens when typechecking this code?
>       EApp (EDouble 10.0) (EPrim "inc")
>
> The error has to be reported somehow, and it is important to know how.
> There are basically two choices. One is to keep the above interface
>
> > test :: Term Double
> > test = typecheck $ EApp (EPrim "inc") (EDouble 10.0)
>
> which means using Template Haskell and means letting TH reporting
> type errors (with no hope of catching them). The second choice is
> `typing dynamic typing'. Which means we can't write a typechecker with
> the signature "Exp -> Term t". Rather, we have to write
>       typecheck :: Exp -> (forall t. Typ t -> Term t -> w) -> Maybe w
> or, equivalently, using an existential
>       data MostlyStatic = forall t. MostlyStatic (Typ t, Term t)
>       typecheck :: Exp -> Maybe MostlyStatic
>
> Although both MostlyStatic and Exp are `untyped', the latter is
> `deeply' untyped, whereas the former is only untyped at the
> surface. In the case of MostlyStatic, the term is built out of typed
> components, and the type is erased only at the end.
>
> These choices aren't contradictory: using TH, we can convert
> MostlyStatic to the proper Haskell term. Because the term has already
> been typechecked, we are guaranteed the absence of errors during such
> a conversion.
>
> For today, perhaps the implementation of the second choice will be
> sufficient. This is the complete representation of a typed DSL given
> in the untyped AST form. We typecheck a term. We either report a
> type error, or evaluate the typed term and then report result, if can be
> shown. We also show the inferred type of the result.
>
> Examples. Let's assume the following terms (not all of them well-typed)
>
> te1 = EApp (EPrim "inc") (EDouble 10.0)
> te2 = EApp (EDouble 10.0) (EPrim "inc")
> te3 = EApp (EApp (EPrim "+")
>            (EApp (EPrim "inc") (EDouble 10.0)))
>            (EApp (EPrim "inc") (EDouble 20.0))
>
> te4 = EApp (EPrim "rev") te3
> te5 = EApp (EPrim "rev") (EApp (EPrim "show") te3)
>
>
> *Eval> teval te1
> "type Double, value 11.0"
> *Eval> teval te2
> "Type error: Trying to apply not-a-function: Double"
> *Eval> teval te3
> "type Double, value 32.0"
> *Eval> teval te4
> "Type error: incompatible type of the application: (String->String) and
> Double" *Eval> teval te5
> "type String, value 0.23"
>
>
> The complete code follows
>
> {-# OPTIONS -fglasgow-exts #-}
>
> -- Typechecker to GADT
> -- Implementing a typed DSL with the typed evaluator and the
> -- the typechecker from untyped terms to typed ones
>
> module Eval where
>
> -- Untyped terms (what I get from my parser):
>
> data Exp =  EDouble Double |
>             EString String |
>             EPrim String |
>             EApp Exp Exp deriving (Show)
>
> -- Typed terms:
>
> data Term a where
>   Num :: Double -> Term Double
>   Str :: String -> Term String
>   App :: Term (a->b) -> Term a -> Term b
>   Fun :: (a->b) -> Term (a->b)
>
>
> -- Typed evaluator
>
> eval :: Term a -> a
> eval (Num x) = x
> eval (Str x) = x
> eval (Fun x ) = x
> eval (App e1 e2) = (eval e1) (eval e2)
>
>
> -- Types and type comparison
>
> data Typ a where
>     TDouble :: Typ Double
>     TString :: Typ String
>     TFun    :: Typ a -> Typ b -> Typ (a->b)
>
> data EQ a b where
>     Refl :: EQ a a
>
> -- checking that two types are the same. If so, give the witness
>
> eqt :: Typ a -> Typ b -> Maybe (EQ a b)
> eqt TDouble TDouble = Just $ Refl
> eqt TString TString = Just $ Refl
> eqt (TFun ta1 tb1) (TFun ta2 tb2) = eqt' (eqt ta1 ta2) (eqt tb1 tb2)
>  where
>    eqt' :: (Maybe (EQ ta1 ta2)) -> Maybe (EQ tb1 tb2) ->
>            Maybe (EQ (ta1 -> tb1) (ta2 -> tb2))
>    eqt' (Just Refl) (Just Refl) = Just Refl
>    eqt' _ _ = Nothing
> eqt _ _ = Nothing
>
> instance Show (Typ a) where
>     show TDouble = "Double"
>     show TString = "String"
>     show (TFun ta tb) = "(" ++ show ta ++ "->" ++ show tb ++ ")"
>
>
> -- Type checking
> data MostlyStatic = forall t. MostlyStatic (Typ t, Term t)
>
> -- Typing environment
> type Gamma = [(String,MostlyStatic)]
>
>
> -- Initial environment (the types of primitives)
>
> env0 = [("rev", MostlyStatic (TFun TString TString,
>                               Fun (reverse::String->String))),
>         -- sorry, no polymorphism!
>         ("show", MostlyStatic (TFun TDouble TString,
>                                Fun (show::Double->String))),
>         ("inc",  MostlyStatic (TFun TDouble TDouble,
>                                Fun ((+ (1.0::Double))))),
>         ("+",    MostlyStatic (TFun TDouble (TFun TDouble TDouble),
>                                Fun ((+)::Double->Double->Double)))
>        ]
>
> typecheck :: Gamma -> Exp -> Either String MostlyStatic
>   -- literals
> typecheck _ (EDouble x) = Right $ MostlyStatic (TDouble, Num x)
> typecheck _ (EString x) = Right $ MostlyStatic (TString, Str x)
> typecheck env (EPrim x) = maybe err Right $ lookup x env
>   where err = Left $ "unknown primitive " ++ x
> typecheck env (EApp e1 e2) =
>   case (typecheck env e1, typecheck env e2) of
>     (Right e1, Right e2) -> typechecka e1 e2
>     (Left err, Right _)  -> Left err
>     (Right _,  Left err) -> Left err
>     (Left e1,  Left e2) ->  Left (e1 ++ " and " ++ e2)
>
>
> -- typecheck the application
>
> typechecka (MostlyStatic ((TFun ta tb),e1)) (MostlyStatic (t2,e2)) =
>   typechecka' (eqt ta t2) tb e1 e2
>  where
>   typechecka' :: Maybe (EQ ta t2) -> Typ tb -> Term (ta->tb) -> Term t2 ->
>                  Either String MostlyStatic
>   typechecka' (Just Refl) tb e1 e2 = Right $ MostlyStatic (tb,App e1 e2)
>   typechecka' _ tb e1 e2 =
>      Left $ unwords ["incompatible type of the application:",
>                      show (TFun ta tb), "and", show t2]
>
> typechecka (MostlyStatic (t1,e1)) _ =
>     Left $ "Trying to apply not-a-function: " ++ show t1
>
>
>
> -- tests
>
> te1 = EApp (EPrim "inc") (EDouble 10.0)
> te2 = EApp (EDouble 10.0) (EPrim "inc")
> te3 = EApp (EApp (EPrim "+")
>              (EApp (EPrim "inc") (EDouble 10.0)))
>            (EApp (EPrim "inc") (EDouble 20.0))
>
> te4 = EApp (EPrim "rev") te3
> te5 = EApp (EPrim "rev") (EApp (EPrim "show") te3)
>
>
> -- typecheck-and-eval
>
> teval :: Exp  -> String
> teval exp = either (terror) (ev) (typecheck env0 exp)
>  where
>   terror err = "Type error: " ++ err
>   ev (MostlyStatic (t,e)) = "type " ++ show t ++ ", value " ++
>        (tryshow (eqt t TString) (eqt t TDouble) (eval e))
>   tryshow :: Maybe (EQ t String) -> Maybe (EQ t Double) -> t -> String
>   tryshow (Just Refl) _ t = t
>   tryshow _ (Just Refl) t = show t
>   tryshow _ _ _ = "<fun>"


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to