Quasi quoting

Max Bolingbroke batterseapower at hotmail.com
Mon Feb 1 09:25:05 EST 2010


Dominic Orchard and I have come up with a rather radical proposal for
a redesign of the syntax. There are two principal options:

OPTION 1 (preferred)
===============

Advantages:
1) QuasiQuotes are revealed as they really are - as splices. In my
opinion this is much less confusing, because a "quasiquote" is really
about generating *code*, like a $(), not about generating a *data
structure* like the existing [|e|], [t|t|] and [d|d|].
2) Unifies Template Haskell and QQ into one construct
3) QQ looks like "semantic brackets"
4) No list comprehension ambiguity

Disadvantages:
1) Small syntax changes to QQ and TH. Increased verbosity in some common cases.

Start with GHC Haskell. Remove [|e|], [t|t|], [d|d|] and [e|..|] syntax.

Add this new syntax:

Syntax: [|...|]
Type: String
Translation: "..." (i.e. this is an alternative string literal syntax)

Now change the semantics of splice, $(e), like so:
 1) If e :: Q Exp and we are in an Exp context in the code, run the
computation and splice the resulting code in
 2) (.. similarly if e :: Q Typ in a Typ context or Q [Decl] in a Decl
context. NB: this is what we had to do for TH before anyway)
 3) If e :: QuasiQuote then select the appropriate field from the
evaluated "e" based on the context, run the Q computation it contains,
and splice the resulting code in

Where:

data QuasiQuote = QuasiQuote {
   quoteExp :: Q Exp
   quotePat :: Q Pat
 }

Now provide exports from Language.Haskell.TH:

e :: String -> Exp
t :: String -> Type
d :: String -> [Decl]

Which parse the provided string as Haskell into the usual data
structure. Uses of Template Haskell quotes must be rewritten:

[|..|] ==> e [|..|]

[t|..|] ==> t [|...|]

[d|...|] ==> d [|...|]

QuasiQuotes now look like:

[foo|...|] ==> $(foo [|...|])

Where foo :: String -> QuasiQuote and defines the language you want to parse.


OPTION 2 (not so good)
=================

Advantages:
1) Normal Template Haskell use looks almost the same as before
2) QuasiQuotes are revealed as they really are - as splices
3) Unifies [t| ... |], [d| ... |] and QQ into one construct

Disadvantages compared to option 1:
1) [| |] is still a special case
3) QQ doesn't look like semantic brackets
4) List comprehension ambiguity remains

As GHC Haskell, but with a new interpretation for the QuasiQuote syntax.
Syntax: [e1| ... |]
Types: if e1 :: String -> a, [e1| ... |] :: a
Translation: e1 "..."

Preserved TH syntax: [| ... |]
Type: [| ... |] :: Exp
Translation: ADT representing "..." parsed as a Haskell program

Adopt the new semantics of $() exactly as in option 1.

Now any existing uses of QQ should be rewritten as:

[foo| ... |] ==> $([foo| ... |])

(You could also allow $[foo| ... |] - i.e. you may omit the brackets)

In this proposal, you can then export "t" and "d" functions from
Language.Haskell.TH with the type:

t :: String -> Type
d :: String -> [Decl]

Which parse the provided string as Haskell. This allows existing any
uses of Template Haskell to remain *unchanged* (as long as they
imported the TH module :-). Otherwise rewrite them as:

[t|..|] ==> Language.Haskell.TH.t [|...|]

[d|...|] ==> Language.Haskell.TH.d [|...|]

(You could potentially special case these in the compiler to generate
the result of the parse at compile time, rather than running the
parser at runtime. This means that the staging behaviour of TH quotes
can stay unchanged)


CONCLUSION
===========

At the cost of changing the staging behaviour of [| |], [t| |] and [d|
|] (usually, the parsing is done at compile time - in my proposal it
is mostly done at runtime) and slightly changing the syntax:
 1) QQ becomes an explicit splice, which is what it should have been
in the first place.
 2) QQ is revealed as the combination of two features: a new notation
for String literals, and some extra overloading of the $() operator to
deal with the QuasiQuote record

I rather like this proposal, even though I realise the chances of such
a radical option being adopted are rather low.

Cheers,
Dominic and Max

2010/2/1 Simon Peyton-Jones <simonpj at microsoft.com>:
> Dear GHC users
>
> This email is to announce two proposed changes to GHC's quasi-quotation mechanism.  For all I know, no one is using quasi-quotation (although it's a very cool feature, thanks to Geoff Mainland), but I didn't think I should take it for granted!
>
> The current spec is here:
>        http://haskell.org/haskellwiki/Quasiquotation
>        http://www.haskell.org/ghc/docs/latest/html/users_guide/template-haskell.html#th-quasiquotation
>
> A quasi-quote can appear as a (a) expression (b) pattern, and looks like this
>        [$pads| ...blah... |]
>
> where 'pads' (of course any name will do) is a record of functions
>   data QuasiQuoter = QuasiQuoter {
>     quoteExp :: String -> Q Exp
>     quotePat :: String -> Q Pat
>   }
>
> The idea is that GHC evaluates (pads "...blah..."), and splices in the resulting Exp (or Pat) just as if that's what the user wrote in the first place.
>
> Kathleen Fisher has started to use this for her PADS system, and came up with two suggestions.
>
> 1. Allow quasi-quotes at the (top-level) declaration level, just like TH splices. So you could say, at top level
>        [$pads| ...blah... |]
> and have it expand to a bunch of top level Haskell declarations. This seems like an unconditionally good idea. To support it we'd need to add a field to QuasiQuoter:
>   data QuasiQuoter = QuasiQuoter {
>     quoteExp :: String -> Q Exp
>     quotePat :: String -> Q Pat
>     quoteDec :: String -> Q [Dec]
>   }
> but I don't think anyone will lose sleep over that.
>
> 2.  Make the notation less noisy for the "customer".  In particular, that '$' is scary, and redundant to boot.  She would like to write
>        [pads| ...blah... |]
>
> I can see the motivation here, but there are two reasons for caution.
>
>  (i) The Template Haskell quote forms [t| ... |] and [d| ... |] behave
>      rather differently.
>
>  (ii) If "[pads|" is a lexeme, then some list comprehensions become illegal, such
>       as  [x|x<-xs,y<-ys].  But note that because of Template Haskell quotations,
>       a comprehension [t|t<-ts] is already broken, and similarly with 'd', 'e'.
>       So the proposed change will make things *more* uniform, by grabbing every
>       "[blah|" as lexeme.
>
> For me (i) is the main issue.  The differences are significant.
>  - A TH quote can appear only where an *expression* is expected
>    But a quasiquote can be an expression or pattern or (assuming (1)) declaration
>
>  - A TH quote has type (Q Typ) or (Q [Dec]) or (Q Exp)
>    But a quasiquote is run immediately and spliced in place of the quote
>
>  - A TH splice is run during type checking
>    But a quasiquote is run during renaming
>
> Even given all that, I'm strongly inclined to follow Kathleen's suggestion:
>  - The differences are there all right, but in some ways the programmer thinks
>    the same way:  [lang| blah |] switches to language 'lang'.
>
>  - Many users will never encounter the issue; they'll just say
>        [pads| blah |]
>    to wake up the PADS magic, and be oblivious to Template Haskell quotes
>
> An alternative would be to have some other cue. Ones I've considered
>
>  - $[pads| ...|], but allowing the $ to be omitted on top-level declarations,
>    top level, just as it now can for TH splices.
>
>  - [pads:| ... |], with the colon distinguishing quasi-quoting from TH.
>
> My gut feel is to go with [|pads| ... |].  Of course this'd be a change from the current syntax, but I think there are few enough users that they'll switch easily enough.
>
>
> Any comments on any of this?
>
> Simon
>
>
>
>
> _______________________________________________
> Glasgow-haskell-users mailing list
> Glasgow-haskell-users at haskell.org
> http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
>
>


More information about the Glasgow-haskell-users mailing list