For some time now I've been saying that Tim and I are working on the design of some enhancements to Template Haskell. It's taken longer than I'd hoped, and we aren't done yet, but you can find a draft at http://research.microsoft.com/~simonpj/tmp/notes.ps The message below is a case in point. There's really no good way to do this in TH today, but it's one of the points addressed in our new design (see Section 5). Comments very welcome, folks. Nothing implemented yet, but it's getting near the front of the queue now. Simon | -----Original Message----- | From: glasgow-haskell-users-bounces@haskell.org [mailto:glasgow-haskell-users- | bounces@haskell.org] On Behalf Of MR K P SCHUPKE | Sent: 29 October 2003 17:46 | To: glasgow-haskell-users@haskell.org | Subject: Template Haskell... | | | Okay, I have got my template working, but its extremely unreadable... could | anyone tell me how to make this look more like normal Haskell code? | | stringType :: String -> Q [Dec] | stringType s = do | x <- gensym "x" | return [ | Newtype [] s [] (Constr s [(NonStrict,Tcon (TconName "String"))]) [], | Proto ("show"++s) (Tapp (Tapp (Tcon Arrow) (Tcon (TconName s))) (Tcon (TconName | "ShowS"))), | Fun ("show"++s) [Clause [Pcon s [Pvar x]] (Normal (App (Var "showString") (Var x))) []], | Instance [] (Tapp (Tcon (TconName "Show")) (Tcon (TconName s))) [ | Fun "showsPrec" [Clause [Pwild,Pvar x] (Normal (App (Var ("show"++s)) (Var x))) []]] ] | _______________________________________________ | Glasgow-haskell-users mailing list | Glasgow-haskell-users@haskell.org | http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
For some time now I've been saying that Tim and I are working on the design of some enhancements to Template Haskell. It's taken longer than I'd hoped, and we aren't done yet, but you can find a draft at http://research.microsoft.com/~simonpj/tmp/notes.ps
Great stuff! I've recently done some TH work again, so let me add something to the wish-list: Another thing that I'm really missing with the current Template Haskell is a "decent" way to pass state between different meta-functions. Let me provide a few examples of the things I want to do: I have one meta-function that generates declarations; and I have another meta function that needs to know what definitions generated by the first meta function there are (accross module boundaries). In my case, the first meta function would declare an Objective-C class and the second one would register all classes with the Objective-C runtime. Manuel Chakravarty provided me with a nice workaround involving the use of (unsafePerformIO $ newIORef ...) global variables from the Q monad. But to make that scheme work transparently, I would still need things like reifyImportedModules :: [String] -- or Q [String] reifyCurrentModuleName :: String -- or Q String And of course, using unsafePerformIO is generally not a nice thing to do, so if somebody could think of a more general solution, that would be great. Another "ugly" idiom that I've been using a lot: I have a function declareClass :: String -> String -> Q [Dec]. $(declareClass "Bar" "Foo") declares Haskell data types for an Objective-C class "Bar", which is derrived from another Objecive-C class "Foo". But it also defines super_Bar = "Foo" because another meta-function will require that information. The identifier "super_Bar" will never be explicitly used by the user of my template library. So I have lots of identifiers where the associated meta-information is stored in separate variables whose name is somehow based on the original name. It works, but all that text manipulation feels more like shell scripts than Haskell... Cheers, Wolfgang
Another thing that I'm really missing with the current Template Haskell is a "decent" way to pass state between different meta-functions. Let me provide a few examples of the things I want to do:
[...]
Manuel Chakravarty provided me with a nice workaround involving the use of (unsafePerformIO $ newIORef ...) global variables from the Q monad.
This is an absolutely essential feature. It's a critical part of Template Greencard. Unfortunately, the workaround (which is the same one I use) doesn't work with ghc --make because the global variable doesn't get reset between modules. So it tends to contain information from compiling whichever random set of modules needed recompiling. The workaround for this is not to use --make. It would also be nice to be able to pass state _between_ modules. For example, in Template Greencard, I want to declare Data Interface Schemes (rules for how to marshal objects to/from C) in one module and use them in another module. This would, presumably, have to be done by providing a hook where my TH code could provide things to include in .hi files and provide a way for my TH code to ask ghc for them back. (This may be the same thing that Wolfgang is asking for.) -- Alastair Reid www.haskell-consulting.com
More things for the TH wish-list: I've been using TH to create language extensions. Unfortunately, the error messages are horrible because: 1) They contain a header generated by TH. This contains useful information like line numbers but it talks about TH instead of talking about my extension. I want to be able to format the error message header myself using information like filename and line number that TH provides to me. 2) They contain a body generated by TH. If the code my language extension generates is wrong, there's virtually no chance that any user (myself included) will figure it out from seeing the machine-generated Haskell code or being given the type error message. Better just to say there is a problem and leave it at that. I want to be able to construct the error message body myself using information like the expression being spliced before evaluation, after evaluation and the error message produced. A related issue is that I generate files corresponding to each TH module I compile. If ghc is producing a file called foo/bar.o, I'd like the name of the file to be foo/bar_stub.o. For this sort of application, I need access to the name of the input and output filenames. Finally, can you drop the 'tick' from variable names produced using gensym? If you'd used underscore, the variable name would be a legal C identifier too. (Ok, so this is a bit specialized but it's very easy to do.) -- Alastair Reid www.haskell-consulting.com
I've been using TH to create language extensions. Unfortunately, the error messages are horrible because:
In case it wasn't clear, the error messages are fine if what you think you're doing is writing a TH module. The problem is that I don't want people to think of my language extensions as TH but just as some black box which does something useful for them. Having error messages that talk about TH and try to explain the problem from a TH viewpoint detracts from that. -- Alastair Reid www.haskell-consulting.com
The other big thing on my wish list is to relax the requirement that templates go in a separate module from code that invokes the template. In Template Greencard, this is a major hassle because you have to put the code to handle C types (you typically define a new template for each C type) in a separate module from the code to handle C functions. -- Alastair Reid
Hi Simon, I realise that the following request might not, for good reasons, get implemented in Template Haskell either, but I think the ability to reify entire modules or reify functions from any module (including standard libraries) would be very useful to any person interested in language extension. However, I realise that there are philosophical and implementation oriented problems with the idea. Firstly, being able to reify code from another module means that all .hi files would bloat to many times their size as they now have to contain reified versions of the declarations in the modules and all this just in case someone happens to want to reify the declaration somewhere else. Also, I realise that this partially breaks the abstraction mechanism of modules. One reason that we have modules and interfaces is so that we don't have to (or even *can't*) look inside the implementation of a module. So how do I justify this feature being added? Well as for the implementation difficulty I can't say that I've thought of a solution to this problem, but I think there is a philosophical difference between the user who is snooping around in modules they shouldn't be and the hard-working, honest meta-programmer who is using a program (not doing it themselves!) to manipulate code in another module. In many cases they will be performing the arduous task of language extensions which is not exactly a task for beginners. Should they not have extra rights? I will write a more reasoned proposal soon. Cheers, Sean
participants (4)
-
Alastair Reid -
Sean Seefried -
Simon Peyton-Jones -
Wolfgang Thaller