[Template-haskell] Using a Typ as a Type

Simon Peyton-Jones simonpj@microsoft.com
Thu, 4 Sep 2003 09:29:39 +0100

OK, I see a bit better now.

One question you raise (implicitly) is whether you can have splices
inside splices:

	y =3D [| 4+5 |]
	f x =3D $(h $y)

TH doesn't currently allow this, but there's no difficulty in principle.
To see this, just substitute for y:

	f x =3D $(h $[| 4+5 |]) =3D $(h (4+5))

But you also try something that really won't work:

	f x =3D $x

This is ill staged.  TH must *run* the top-level splices.  It can't run
$x because it doesn't know x's value yet.

So in your example, the line
	let x =3D f "1" :: $typ
is ill-staged because TH can't run $typ, because type is lambda bound.

I could just about imagine the following. Suppose your 'generate' was a
TH function that *generated a TH function*.  So the entire body of
'generate' is quoted.  Then I could just about imagine how it might
work.  The type of generate would look something like
	| generate :: String -> Q Typ -> Q (Expr (Q [Dec]))

(I'm imagining here that Expr is parameterised, which it isn't, but it
helps documentation to say (Expr t) means an Expr of type t.)

Then a call to generate would look like

	$($(generate ..))

It would execute by running generate to produce a TH program
(essentially inlining only), typechecking that (this is where the
overloading you want would be resolved) and then running it.

A consequence would be that the TH data types (Expr, Dec etc) would need
to get extra constructors to represent quotes, splices etc.

Bottom line: today, definitely no.   But I hadn't realised before that
there could, just, be some benefit to repeated splices like
$($(generate...)).  I wonder if anyone else has come across this need?


| -----Original Message-----
| From: Alastair Reid [mailto:alastair@reid-consulting-uk.ltd.uk]
| Sent: 03 September 2003 16:06
| To: Simon Peyton-Jones; Alastair Reid; template-haskell@haskell.org
| Subject: Re: [Template-haskell] Using a Typ as a Type
| > Perhaps the thing to
| > do is to give a simple but concrete example of what you'd like to
| The following is a simplified version of what I tried to do in
| Greencard.
| -- a new class
| class X a where
|   -- member parameterized on result type
|   f :: String -> a
|   -- member parameterized by argument type
|   ctype :: a -> String
| -- instances for old types
| instance X Int where
|   f =3D read
|   ctype _ =3D "HsInt"  -- FFI-defined C type corresponding to Int
| -- instances for freshly defined types
| newtype T =3D T Int
|   deriving Show  -- included to make the example work
| instance X T where
|   f s =3D T (read s)
|   g (T x) =3D g x
| generate :: String -> Q Typ -> Q [Dec]
| generate nm typ =3D do
|   -- The next line contains the type splice.
|   -- Note that it is used to perform a compile-time dictionary
|   -- lookup not a runtime lookup.
|   -- I'm not wedded
|   let x =3D f "1" :: $typ
|   -- Generate C code (should be written to a file, not stdout)
|   -- Code generated depends on type argument
|   qIO (putStrLn (ctype x ++ " " ++ nm ++ " =3D " ++ show x ++ ";\n")
|   -- return a variable definition.
|   -- again, the definition returned depends on the type
|   -- because it uses 'x' which was produced as a result of
|   -- the compile-time dictionary lookup.
|   [d| $nm =3D $(literal x) |]
| > Remember, the execution of TH program can be described by ordinary
| > rewriting rules (replace the LHS of a function by the RHS of the
| > function, suitably instantiated), augmented with the one extra rule
| > 	$[| e |]  =3D  e
| Should this apply to types as well?
| --
| Alastair