Re: [Haskell-cafe] Tagless interpreter, expression problem and zipper

The Tagless final approach can do context-sensitive evaluation, using the well-known trick from the denotational semantics that explicating the context turns context-sensitive semantics to compositional. The trick isn't out of place given how much tagless-final approach is related to denotational semantics. The non-compositional processing has been discussed at length at lectures last year. Please see the explanation and the code at Non-compositionality: Fold-unlike processing http://okmij.org/ftp/tagless-final/course/course.html#non'compositionality Interestingly, one example FlatF.hs mentioned on the above page specifically talks about the context of being the left operand of the addition (the goal was to `flatten' additions, to re-associate them to the right). That is the same context as you are after, it seems. The detailed lecture notes will be eventually available.

Both your replies where very helpful. I combined both approaches to get nearer to what I want.
class Lit α where lit ∷ Integer → α class Add α where add ∷ α → α → α
instance Lit Integer where lit = fromInteger instance Add Integer where add = (+)
This time I require TypeSynonymInstances:
instance Lit String where lit = show instance Add String where add x y = "(" ++ x ++ " + " ++ y ++ ")"
Felipe's generalized context type:
data AddCtx α = Empty | AddL α (AddCtx α) | AddR α (AddCtx α)
Combination of Oleg's tagless transformer and Felipe's CtxInterpB:
instance (Add α, Add β) ⇒ Add (AddCtx β → α, β) where add (xa, xb) (ya, yb) = ( \c → add (xa (AddL yb c)) (ya (AddR xb c)) , add xb yb ) The previous instance allows me to construct Strings while having Integers in the context.
Silly interpreter, version 2.0
instance Lit (AddCtx Integer → String, Integer) where lit n = ( \c → case c of AddL 3 _ → "Foo!" _ → lit n , lit n )
Simple term:
t1 ∷ (Lit α, Add α) ⇒ α t1 = lit 2 `add` lit 3
Interpret as a String:
bar = let (f, x) = t1 ∷ (AddCtx Integer → String, Integer) in f Empty
"(Foo! + 3)"
This is already an improvement to my current code. But I am not entirely satisfied. I can pick and choose which structures to use in my terms but the context type is still an ordinary data type. Each module which extends the expression language with new structures needs to define a complicated context type. My plan is to define the context as a type class. Obviously I can't perform case analysis on a polymorphic type so I'll have to add that functionality to each context class in some way. Thank you for your helpful replies!

This is already an improvement to my current code. But I am not entirely satisfied. I can pick and choose which structures to use in my terms but the context type is still an ordinary data type. Each module which extends the expression language with new structures needs to define a complicated context type.
But the context type does not have to be complicated. There is no rule mandating the single context for any expression. For different sorts of processing, one may define suitable contexts, containing only the needed data. The example from the earlier message had to do something special for a literal that is the left immediate child of Add whose brother has specific value. That example requires a simple context. Here is the complete code, a small simplification of the code in the previous message.
class Lit r where lit :: Integer -> r class Add r where add :: r -> r -> r
instance Lit Integer where lit = fromInteger instance Add Integer where add = (+)
instance Lit String where lit = show instance Add String where add x y = "(" ++ x ++ " + " ++ y ++ ")"
data AddLCtx r = AddL r | NotAddL
instance (Add r, Add s) => Add (AddLCtx s -> r, s) where add (xa, xb) (ya, yb) = ( \c -> add (xa (AddL yb)) (ya NotAddL) , add xb yb )
-- Silly interpreter, version 2.0
instance Lit (AddLCtx Integer -> String, Integer) where lit n = ( \c -> case c of AddL 3 -> "Foo!" _ -> lit n , lit n )
t1 :: (Lit r, Add r) => r t1 = lit 2 `add` lit 3
bar = let (f, x) = t1 :: (AddLCtx Integer -> String, Integer) in f NotAddL -- "(Foo! + 3)"
Later on, we add multiplication.
class Mul r where mul :: r -> r -> r instance Mul Integer where mul = (*) instance Mul String where mul x y = "(" ++ x ++ " * " ++ y ++ ")"
We extend the Ctx interpreter for Mul, assuming AddLCtx meant the immediate left child of the Addition. If a left descendant was meant, the mul rule below should propagate the context rather than override it.
instance (Mul r, Mul s) => Mul (AddLCtx s -> r, s) where mul (xa, xb) (ya, yb) = ( \c -> mul (xa NotAddL) (ya NotAddL) , mul xb yb )
t2 :: (Lit r, Add r, Mul r) => r t2 = (lit 2 `add` lit 3) `mul` (lit 4 `mul` (lit 5 `add` lit 3))
bar' = let (f, x) = t2 :: (AddLCtx Integer -> String, Integer) in f NotAddL -- "((Foo! + 3) * (4 * (Foo! + 3)))"
Let's implement the example in the original message, and do something special at the left immediate child of addition whose brother is so and so and whose parent context is the left child of multiplication. We define the context that suits the problem, tracking if we are the left child of multiplication and if we are the left child of addition that is the left child of multiplication. It is easier done than said.
data AddML r = AddML r | MulL | NotAddML
instance (Add r, Add s) => Add (AddML s -> r, s) where add (xa, xb) (ya, yb) = ( \c -> (case c of MulL -> add (xa (AddML yb)) (ya NotAddML) _ -> add (xa NotAddML) (ya NotAddML)) , add xb yb )
instance (Mul s, Mul r) => Mul (AddML s -> r, s) where mul (xa, xb) (ya, yb) = (\c -> mul (xa MulL) (ya NotAddML), mul xb yb)
The original example:
instance Lit (AddML Integer -> String, Integer) where lit n = ( \c -> case c of AddML 3 -> "FooM!" _ -> lit n , lit n )
bar'' = let (f, x) = t2 :: (AddML Integer -> String, Integer) in f NotAddML -- "((FooM! + 3) * (4 * (5 + 3)))"
The fully safe and explicit Typeable in http://okmij.org/ftp/tagless-final/course/Typ.hs shows example of defining many interpreters with many contexts. It was the tough problem though -- the first example showing how to compare to tagless-final terms for equality.
participants (2)
-
oleg@okmij.org
-
Roel van Dijk