1
votes

I had the following Haskell code (just a fragment of all code, but works):

data ByteCode t where
    INT :: Int -> ByteCode Int
    BOOL:: Bool -> ByteCode Bool
    Add :: ByteCode Int -> ByteCode Int -> ByteCode Int
    Mul :: ByteCode Int -> ByteCode Int -> ByteCode Int


newtype C t = C (Int -> (ByteCode t, Int)) 
unC (C t) = t



instance Symantics C where
    int x  = C(\vc -> (INT x, vc))
    bool b = C(\vc -> (BOOL b, vc))

    add e1 e2 = C(\vc -> let (e1b,vc1) = unC e1 vc
                             (e2b,vc2) = unC e2 vc1
                         in (Add e1b e2b,vc2))

    mul e1 e2 = C(\vc -> let (e1b,vc1) = unC e1 vc
                             (e2b,vc2) = unC e2 vc1
                         in (Mul e1b e2b,vc2))

I am trying to convert it to Template Haskell. Specifically I wanted to get rid of the ByteCode and just use ExpQ (new to me) Template Haskell type. So now I have:

    newtype C a = C ExpQ
    
    unC (C x) = x

I'm trying to update the instance, so my new code is:

newtype C t = C ExpQ
unC (C t) = t



instance Symantics C where
    int x  = C(\vc -> (ExpQ x, vc))
    bool b = C(\vc -> (ExpQ b, vc))

    add e1 e2 = C(\vc -> let (e1b,vc1) = unC e1 vc
                             (e2b,vc2) = unC e2 vc1
                         in (Add e1b e2b,vc2))

But I am getting a lot of Couldn't match expected type ‘Q Exp’ with actual type ‘t1 -> (t0, t1)’ errors. How can I best write this new instance?

1
The ExpQ type is just a synonym for Q Exp. There is no constructor named ExpQ as you seem to be trying to use here. It’s not clear what you’re trying to achieve, and a full tutorial on how to use Template Haskell is not suitable for Stack Overflow. Based on the misspelled “Symantics” (and several of your previous questions) it looks like you’re trying to follow along with some of Oleg Kiselyov’s writing on staged tagless final interpreters? Could you share what you’re basing this on and how you expect to use the result?Jon Purdy

1 Answers

1
votes

In the original C type, there was an integer "state" threaded through the computation, meaning that the C newtype actually wrapped a function of type:

Int -> (ByteCode t, Int)

The expressions:

C (\vc -> ...)  -- C constructor takes a function

and

unC e1 vc   -- unC e1 gets the function, and it's applied to vc

in the original code reflected this.

In your new C type, you've removed the state, and the C newtype is just an ExpQ, but you haven't updated any of the code to reflect this change, so you are getting type mismatch errors between ExpQ (the type wrapped by your new C) and functions of the form Int -> (ByteCode t, Int) (the type wrapped by the old C).

You might get closer to your intended goal with something like:

instance Symantics C where
  int x  = C $ litE (IntegerL (fromIntegral x))
  bool False = C [|False|]
  bool True  = C [|True|]
  add e1 e2 = C [|$(unC e1) + $(unC e2)|]