
Hi - here is an exchange that was off-list by accident: ---- Original Message ---- From: Brian Smith To: Brian Hulley Sent: Tuesday, August 22, 2006 5:50 AM
(Brian, I see that my last reply only went to you and so I forwarded it to the list. Since you replied to me directly, I am responding to you directly. Feel free to forward this to the list if you want).
On 8/21/06, Brian Hulley
wrote: The paper "Template meta-programming for Haskell" suggests that Template Haskell should be able to do everything that CPP is used for at the moment, such as conditional compilation, macros etc, so this might be a possible option, though I have to admit apart from reading the paper and doing a few experiments with it I don't know enough about the current state of TH to know if it would be a viable replacement at the moment.
I am very interested in TH and I hope to give it a thorough workout on an upcoming project. Until then, I don't know enough about it to compare it to CPP. But, I think that in an Editor/IDE, a restricted CPP would be much easier to implement than TH.
I've got a feeling that even allowing #define could break the invariant that a module only has one meaning in a program, since #define changes the state of a symbol that's not yet been mentioned just as #undef changes the state of a defined symbol eg:
module P where #define Bar import R
module R where #ifdef Bar import S #else import T #endif
module Q where import R
I think that this program will only compile if it is split into three files, P.hs, R.hs, and Q.hs. There would be two scopes for CPP macros: global (each macro is bound to exactly one value for all modules, and it cannot be undefined or redefined) and file/module (the macro is bound exactly once in a module and that binding is not visible to other modules). Except for the "exactly once" part, this is how GHC and cpphs already work.
So, in your example, let's assume there is no global binding for "Bar"--if there was, then there would be a redefinition error in P.hs. Then, P.hs defines Bar, but this binding is in scope only inside of P.hs--R.hs does not "see" Bar as being defined. Further, since R.hs does not contain a binding for "Bar," so module R will always import module T and never module S.
A more concrete example is this:
module P where #ifdef __GHC__ import P.GHC #else import P.Haskell98 #endif
In your editor/IDE you might want to support multiple Haskell implementations. So, sometimes __GHC__ will be defined (when in GHC "mode") and sometimes it won't. But, I recommend to handle this in the same way that IntelliJ handles the JDK setting. That is, if the user wants to switch the implementation he is using (even temporarily), then when he does so, the whole program gets reanalyzed. If the user wants to work in Hugs mode and GHC mode in the same time, then he should create two projects in the IDE. In one project, the implementation will be set to GHC, and the other one Hugs. He will have to define output paths, etc. such that the two project's ouitput files do not overlap even though their sources will. Then, analyze each project seperately. This technique also allows the user to build a version of the project against, say, HaXml 1.14, and other version against 1.15.
Much as I'd like to not have to deal with CPP, I agree that on a practical realistic level it's not going to go away immediately, but at least the guidelines you posted give a good starting point for library authors to gradually try and reduce the dependence on the preprocessor, if they are motivated to (which they probably aren't ;-) )
Right. Plus, if we follow the guidelines I proposed very strictly, then we can more easily picture what is required of a CPP replacement. For example, if the guidelines are too prohibitive, then we will know we need something more powerful like TH. If the guidelines do not result in any practical loss of functionality, then we know that we can get by with a much simpler configuaration language.
I had a wild idea of a tool to eliminate CPP from a source tree by first generating the set of N pre-processed source trees (ie now pure Haskell) then running some analysis tool over these trees to automatically factor out common code, but this would probably be a major undertaking.
That would definitely be an interesting project, and it would be useful for other things as well (The duplicate code detection part.)
Cabal seems to help a lot - Edison and ByteString both built on my machine with no trouble at all. I think the more everything can be done in Haskell the better it will be.
I agree. But, I also want it to be easy to reuse C-based libraries whenever possible. That way, we can spend more time writing code in Haskell that does new things instead of reimplementing old ideas in it.
You mentioned that library maintainers might not be motivated to make such changes. I think that people will modify their libraries if there are compelling tools that need those modifications. In fact, I think people would even be willing to break previous commitments to backwards compatibility if there was any benefit to doing so--I mean, who really uses the latest version of HaXml with GHC 5.x or Hugs 2002-xx-xx?
If there was an IntelliJ-level IDE for Haskell, and people were writing "real world" programs in Haskell, then somebody would get around to modifying libraries to work for "real world" programs built using such a tool. Or, somebody that had a commercial interest (a Haskell consultant or an IDE vender) might make these modifications.
- Brian