What is the best way to start a GHC proposal

I could use TH but not without all of its drawbacks (I already listed some in my post).
TH would not allow me to write zipField and use it in the same file. I would like to be able to do that.
TH splits your files into sections forcing you to write things in a certain order. I would like to be able to get rid off that.
If we could modify TH somehow to be able sort those two things, then I agree with you, we don’t need a new macro system. Maybe “untyped string” is not the best solution and I’m open to anything.

It is when you can use it, but it stops being useful when you are working with a unknown number of element. Let’s go back to zipField, which take a list of arguments (just not two). So I could do

data Point  = Point {x,y :: Double}
data Point3 = Point3 {x,y,z :: double}

addPoint = Point $((zipField ["x", "y"])
addPoint3 = Point3 $((zipField ["x", "y", "z")

How would you write the equivalent in TH using TH Quotation ? (I’ve done similar things and the code end up being something like foldl AppE (VarE $ mkName fnFame) fs (which looking at it now, I have no idea what it does).

What I want to create a HKD like

data PointF f = PointF { x :: f Double
                                    , y :: f Double
data Point3F f ...

With “untyped string macro” in could write (in a single file), something along

makeF :: String ->  String -> String
pointF cons fields = 
     "data $cons f = $cons  {"
    ++ intercalate "\n,    " [ "$field :: f Double | field <- words fields ]
    ++  "\n }"
makeF "Point" "x y"
makeF "Point "x y z"

It tooks me 2mn to write. How would be the equivalent in TH ?

makeF cons field s=
    DataD [] (mkName cons) ? ?? ? ( foldl AppT ???) []

Every time something is added to GHC, DataD is getting more arguments which I need to fill in with [] or Nothing etc …

Probably but I think TH has show is limits which is why since TH has been introduced we got type families, generics (which wouldn’t have been needed if TH has been easy enough to use).
The last proof of limitation of TH is the multiline proposal.
We already have one native way to do multilines, (using \), then there are plenty of Template Haskell nice solutions to it, with or without string interpolation, with or without leading spaces stripping etc …
If TH was good enough, everybody would be using a TH solution to this problem, but nobody is, thus the need for multilines syntax.

I’m not sure about the specifics but IIUC typed TH is better in this regard. See #12457: Deriving should be (more closely) integrated with other metaprogramming methods · Issues · Glasgow Haskell Compiler / GHC · GitLab and #21051: Stage restriction differs between untyped and typed TH · Issues · Glasgow Haskell Compiler / GHC · GitLab

You didn’t look at my example, did you?

Easy, here’s an adaptation of my earlier example: Haskell Playground


For discussion about a stable interface to TH see


If having pure slices can be a step towards detecting “light” splices (where TH sectioning can be overcome) then I am all for it. Also I was thinking of maybe using $! syntax and obviously both proposal can’t use it, so they need to go hand in hand :slight_smile:

Fair enough. I didn’t look at your Playground that is impressive indeed (and I learn something :-))
But it only proves that you can write it in TH, not that I can lol (and definitely not off the top of my head, which is my main point).

Anyway, I’m just pushing to have a better and easier to use macro system. If that mean improving TH instead then why not.


Semi-related: #20862: Provide basic QuasiQuoters in template-haskell? · Issues · Glasgow Haskell Compiler / GHC · GitLab

1 Like

I’ve been wondering too why standard quasiquoters were not available. Maybe an an easier to implement syntax would to allow to chain quasiquoter (as in [myQQ . e|...|]).

I addition to Tom’s links above, I’d suggest you gave Untyped Template Haskell quotes as patterns (under review) by Ericson2314 · Pull Request #529 · ghc-proposals/ghc-proposals · GitHub a look. Pattern quotes is something that I think we desperately need to write backwards-compatible macros. It should be simpler to inherit this proposal than to write a new one.

1 Like

Thanks you all for the links.

I’m not pushing for untyped string vs Q, just to have a lighter macro system.
By lighter I mean

  • a) no sectioning of the file (order shouldn’t matter) when possible
  • b) being able to use functions definition in the same file
  • c) not having to learn a new “language”: use quotation as much as possible
  • d) avoid unnecessary recompilation
  • e) resiliant to GHC internal changes

I realized that TH is more capable than I though it was (thanks to @rhendric) and there already some discussions going own about how to improve the situation (even though it seems a bit all over the place).
To summarize

The use of let clauses addresses b) in some cases (maybe it could be added explicitely in the wki) but doesn’t allow “in place” definition of quasiquoter (correct me if Y am wrong).

Provide basic QuasiQuoters in template-haskell might helps.

#529 Untyped Template Haskell quotes as patterns seems promising and could help with c) and e).

Introducing a difference between pure and impure splices (as @michealpj suggested ) could help with d) (if there is really a problem there): A pure splices should only depends on the input files and as such needs only need to follow “normal” file recompilation rules.

This leave a) (which I believe is called “stage restriction” unless it is the name for b) or both). I am still not sure if this problem
is only the result of implementations detail or if there is something deeper to it and if there is a difference between splies $(..) and quasi quoting ([|...|])
But my intuition is that things like using quasi quoting to do string interpolation or precalculating constants should be able to work without affecting the order in which things are defined.

Finally, there seems to be two discussions regarding stability ( point e)) one on gitub and one on gitlab
Which one is the most relevant ? Do we need something to coordinate (tidy?) all those effort ?

You proved me quotations were more convenient that I remembered so I gave it a look again.

It doesn’t seem that you can use $... in data declaration, to do things like

let a = "A" in [d| data $a = $a |]
-- or
let i = "Int" in [d| data A = A $int |]

Am I missing somethnig or is it not possible ?
If not that is a new argument in favor of string based macro.

I think the first does require the TH API; splices have to be expressions, patterns, types, or entire declarations, and what you want to splice there is none of those things (maybe this list could be expanded).

The second can be done with just quotes:

  i = [t|Int|]
  in [d|
    data A = A $i
1 Like

In defense of staging: any macro system, be it Template Haskell or some new thing you come up with, is going to have to contend with the dueling desires that some people will want to use (not just mention, but invoke or construct) in their macro code a function or type defined normally in the same module, and other people will want to use in normal code a function or type defined in a macro in the same module. Staging makes both patterns possible—you can define types and then macros that use those types and then functions that use those macros and then more macros that use those functions and so on.

Without staging, I don’t think there’s a way to achieve this. Your proposed solution of running all the macros first and then compiling the normal code certainly doesn’t—if running the macros requires running the normal code, you can’t wait until after the macros run to compile the normal code.

But with staging, you can choose to implement the pattern of macros-first-then-normal-code simply by, well, putting all your macros before your normal code. If you are only mentioning, and not using, names from the normal code in your macros, there is no issue with staging per se, other than the purely aesthetic complaint of perhaps not wanting all of your macros to come first. That seems like a very small price to pay for the flexibility that staging gives other people with other macro-use-pattern desires. It also seems like a very small price to pay to keep everyone on one macro system where we can pool our bug-fixing, documenting, and educating efforts, instead of having two even-more-poorly documented etc. implementations of macros for Haskell users to choose between.

Edit: GHC is probably more restrictive about staging than it needs to be; I’m not arguing that the status quo is ideal. I’m just arguing that aiming for ‘order doesn’t matter’ goes too far.

So how does Clean’s macro system work? Perhaps an explanation of how it works may prove useful to others here.

Re Clean, I suppose I should have said ‘any macro system supporting arbitrary compile-time computation’ instead of ‘any macro system’.

…anyone for Safe Template Haskell ?

So I don’t know Clean, and the website isn’t very detailed. But from the online guide, it looks like Clean doesn’t support calling external functions in a macro. The only example there shows calling a function defined in a where block.

Thanks for that (I had wondered if anyone knew about the 3.1 release’s macro system). So Clean 2.2’s macro system ensures that:

The compile-time substitution process is guaranteed to terminate.

Is there a similar mechanism for TH? Or does “arbitrary” include ?

Oh ho ho, not only does it include divergence, it includes anything you can do with IO and, as has already been linked in this thread, breaking out into the compiler internals.

It’s a good thing play.haskell.org does both its compilation and its execution in a sandbox.

…yeah, thanks - I was thinking more about Turing-completeness than that other “ugly stuff” (which I had already seen). But having all that together in one convenient “bite-sized” answer will also be useful…as a warning to future implementors.