Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type annotated quoters #125

Merged
merged 10 commits into from Aug 1, 2019

Conversation

winterland1989
Copy link
Contributor

@winterland1989 winterland1989 commented Apr 19, 2018

The proposal has been accepted; the following discussion is mostly of historic interest.


Rendered

Rendered as rewritten by @isovector

@gelisam
Copy link

gelisam commented Apr 19, 2018

How about reusing the existing phantom typed TExp instead of introducing a second one? That is, let's use TExp a instead of Exp:

data QuasiQuoterExp a = QuasiQuoterExp (String -> Q (TExp a))

@winterland1989
Copy link
Contributor Author

@gelisam I remember reading somewhere that untyped template is needed because typed one are not always work, but i'm not sure about this. Of course if we can reuse TExp a, then yes please. I think i need some answers from GHC HQ.

@gelisam
Copy link

gelisam commented Apr 19, 2018

I remember reading somewhere that untyped template is needed because typed one are not always work, but i'm not sure about this.

Untyped TH is needed because it is more powerful than typed TH. For example, in category-syntax my macro takes in code which does not type-check, so it cannot have type TExp a for any a. It can have type Exp though.

TExp is just a wrapper around Exp with a phantom type:

newtype TExp a = TExp Exp

So using String -> Q (TExp a) instead of String -> Q Exp would certainly not limit the expressivity of type-annotated quasiquoters.

@winterland1989
Copy link
Contributor Author

@gelisam I see, I have changed the proposal to use TExp a.

@goldfirere
Copy link
Contributor

I'm not much of a consumer of quasi-quoters, so I don't have an informed opinion as to whether this is a good idea.

I do agree that it should use TExp.

@simonpj
Copy link
Contributor

simonpj commented Apr 20, 2018

This proposal makes sense to me. It's just a natural extension of the entire Typed Template Haskell idea, when TExp was first introduced. With that in mind can we call it TQuasiQuoter a please? Just like Exp vs TExp.

It's much more than just documentation!! Consider

qq :: TQuasiQuoter Char

blah = [|qq| unicode 78 |] && True

With the existing quasi-quote machinery we'd first have to run qq, splice in the resulting syntax tree, and then complain if it didn't typecheck. With a typed quasi-quoter we can complain right away: qq returns a TExp Char and the doesn't fit somewhere a Bool is needed.

Worth explaining this in the proposal.

@winterland1989
Copy link
Contributor Author

@simonpj

With the existing quasi-quote machinery we'd first have to run qq, splice in the resulting syntax tree, and then complain if it didn't typecheck. With a typed quasi-quoter we can complain right away: qq returns a TExp Char and the doesn't fit somewhere a Bool is needed.

This's a good point, i have added to Effect and Interactions section of the proposal, also updated to use TQuasiQuoter a instead of QuasiQuoterExp a. I personally don't have any problems with either name, hope not getting too much bikeshedding here.

@gbaz
Copy link

gbaz commented Apr 21, 2018

This proposal doesn't specify if TQuasiQuoter would be run during the renamer (as quasi-quoters are now run) or during the type-checking phase (as typed splices are now run). In this case, I don't see how, given how quasi-quoters work, we could derive any benefit from running in the latter phase in terms of information for the quoter. However, to get the benefit simon suggests (that we check and then expand), I imagine we would have to run in the latter phase anyway?

@winterland1989
Copy link
Contributor Author

@gbaz , TQuasiQuoter a should be run during the renamer just as current QQ, the only difference is that we're splicing TExp a AST node instead of Exp node. The benefit that simon described shoud be a nature result of using TExp a node. i.e. the major part of this proposal has been in place, we only have to add an user interface to support it.

@simonpj
Copy link
Contributor

simonpj commented Apr 24, 2018

TQuasiQuoter a should be run during the renamer just as current QQ,

I don't think so. Just like TExp it should run in the type checker. Somewhere we have to check that the type matches the place it is spliced in, and the type checker is the only place to do that. I'm arguing for a tight connection with TExp.

@akhra
Copy link

akhra commented Apr 25, 2018

Pardon the tangent, especially since I'm only following half of this; but I can't help wondering if there might be some potential cross-pollination with Hackett here, since its whole purpose for existence is to unify type checking with macro expansion. That's not the same as unifying with TH, so it's not directly applicable to this proposal; but I think it solves the same problem?

Of course, the impedance mismatch might simply be too strong. But @lexi-lambda has mentioned that if it were more viable/convenient, she would like GHC as a Hackett backend, at which point Hackett modules should be importable from Haskell and vice versa, right? What's in the way of that? Is it harder than working out all the kinks and details here?

Again, pardon the derailment, this isn't really the right place to bring this up. But it is the right context and audience, and I'm not sure where else to find that!

@winterland1989
Copy link
Contributor Author

@simonpj I'm definitely wrong on how a TExp a node got spliced, thanks for correcting me on this! I have added some notes under the proposal's Proposed Change Specification section to make this point clear.

@winterland1989
Copy link
Contributor Author

@nomeata I think this is ready for committee review ; )

@nomeata nomeata added the Pending committee review The committee needs to evaluate the proposal and make a decision label May 6, 2018
@nomeata nomeata assigned mchakravarty and unassigned simonpj May 14, 2018
@nomeata
Copy link
Contributor

nomeata commented May 14, 2018

Manuel volunteered to shepherd this.

@winterland1989
Copy link
Contributor Author

winterland1989 commented May 14, 2018

OK, let's see if my friends @sighingnow got some time. (Sorry for clicking the close button by mistake)

@goldfirere
Copy link
Contributor

I got an email from @simonpj that he was on holiday today. I don't know whether this extends for the whole week. I would love to hear more of his input, though it seems that his primary concern has been addressed.

@nomeata
Copy link
Contributor

nomeata commented Jul 22, 2019

/remind me that @isovector wants to accept this on July 29

@reminders-prs
Copy link

reminders-prs bot commented Jul 22, 2019

@nomeata set a reminder for Jul 29th 2019

@ghc-proposals ghc-proposals deleted a comment from reminders-prs bot Jul 22, 2019
@simonpj
Copy link
Contributor

simonpj commented Jul 25, 2019

Iavor's example is indeed helpful. I now see this proposal primarly as a way to write literal values in user-defined syntax. All the examples have this form:

  • [qq|| unicode 78 ||] is user-defined syntax for a Char literal. (This from the proposal.)
  • [qq1|| abcdefgh ||] is user defined syntax for a Char->Char function that does funny rotation.
  • [checkedNum|| 241 ||] is user-defined syntax for an Integer` whose bouds are checked statically
  • [lam|| blah ||] is user-defined syntax for a literal of type Expr; this syntax may be more convenient than writing lots of Fun, Add and Var nodes.

Remember that in every case, we have an alternative that works today, namely

  • $$(qq "unicode 78")
  • $$(qq1 "abcdefgh")
  • $$(checkedNum "341")
  • $$(lam "blah")

Is the quasiquote version sufficiently better? Beyond the name of the quasiquoter, the syntactic baggage is

  • 6 characters for an explicit splice, namely two dollars, two parens, two quotes
  • 6 characters for a quasiquote, namely open and close brackets and four vertical bars.

Here I'm assuming that the typed version of quasiquotation uses two vertical bars, just like the type version of TH quotes. I suppose that we could be clever, and switch between typed and untyped quasiquoters based on the type of the quasiquoter, but that's uncomfortable. (The untyped ones run in the renamer which knows nothing of types.)

Either way, the difference is not large. Question: does this proposal really pay its way?

One might ask the same question of the existing quasiquotes, which can be similarly replaced. with splices. Back in 2007 when they were designed, all TH splices ran in the typechecker, and quasiquotes (which must run in the renamer) were something new. But nowadays, with the clear untyped-TH vs typed-TH split, I think we might well reject quasiquotes as a language extension too. Maybe I should write a proposal to deprecate quasiquotes!

TL;DR, this proposal makes the language (and its implementation) just a bit more complicated, in exchange for what amounts to a minor syntactic variation in concrete syntax. I have not heard anyone saying "my life woud be so much better with this".

So if it were me I wouldn't do it. But if everyone else is keen I won't stand in the way. The strongest argument is that (for better or worse) we do currently have quasiquotes, and this adds them to the typed-TH box as well as the untyped-TH box.

If it is accepted, could the proposal be modified:

  • Stress that this only applies to expression quasquotes. For patterns, types, and declarations you only have the untyped version.
  • Make explicit the concrete syntax: two vertical bars or one?

@parsonsmatt
Copy link
Contributor

parsonsmatt commented Jul 25, 2019

Quasioquotes are nice because they don't deal with weird escaping rules, work great over multiple lines, and are syntactically much easier. Replacing [quote| ... |] with $$(quote "...") would dramatically harm almost every use of QQ that I'm aware of.

Consider a JSON quasiquoter.

someJson :: Value
someJson = [json|
    { "hello world": [ 
        "look ma", 
        { "no weird quote": "escaping rules to follow!" } 
        ] 
    } 
|]

If we were to redo this with a string literal, it'd look like garbage instead:

someJson :: Value
someJson = $$(json "\
 \   { \"hello world\": [ \
 \      \ "look ma\", \
 \      { \"no weird quote\": \"escaping rules to follow!\" } \ 
 \      ] \ 
\   }"

This entirely breaks the property of QQs that I can copy/paste some DSL directly into a quoter and get a Haskell value out of it.

@simonpj
Copy link
Contributor

simonpj commented Jul 25, 2019

That's a very helpful observation, thanks Matt. But it's kind of accidental. You are really saying that Haskell lacks a good notation for string literals, especially

  • Line breaks
  • The need to escape double quotes

and it happens that the way that quasiquotes delimits strings is better for very long strings. Nothing directly to do with quasiquotation at all.

Indeed [pure|| blah] :: String, where pure:: String -> Code String is perhaps a useful way to get a string literal! Instead of "foo \"bar\"" you could write [pure|| foo "bar"|].

It's really good to hear from people using this stuff in the field. More please!

@bitemyapp
Copy link

bitemyapp commented Jul 25, 2019

@simonpj https://gitlab.com/lorepub/moot/blob/master/src/Model.hs#L28-135

This is how the database models have been defined on almost every project I've worked on in the last 5 and 1/2 years, save the one that was Opaleye.

I also use quasiquoters for:

I'm not really sure what you're angling at but it unnerves me.

@simonpj
Copy link
Contributor

simonpj commented Jul 25, 2019

I'm not really sure what you're angling at but it unnerves me.

Don't be un-nerved! I'm just pointing out that, in principle, a quasiquote [foo| blah|] can always be written $(foo "blah"), which is not so very different.

But Matt points out that a quote string "..." has different multi-line and escape conventions to the string captured by a quasiquote [|| ...|]. And in practice that makes a very big difference indeed.

I had not fully appreciated that because I'm not an at-scale user of quasiquotation, so I was expressing apprection of some concrete examples from the front line, which you have helpufully added to.

No more than that. To me, this says that we should have designed a better notation for string literals in the first place!

@gridaphobe
Copy link
Contributor

Indeed, quasiquoters are so much nicer than our notation for multi-line string literals that I've seen at least one package that provides a quasiquoter that is just for string literals (e.g. the q quasiquoter from https://hackage.haskell.org/package/interpolatedstring-perl6-1.0.1/docs/Text-InterpolatedString-Perl6.html).

@Ericson2314
Copy link
Contributor

Ericson2314 commented Jul 26, 2019

👍 to these recent messages. I maintain this proposal is good while we have quasiquoters, but I actually despise them and would love to see them deprecated and removed:

  • As mentioned, they elide the natural separation of concerns between parsing and splicing.

  • The interface is a rather ad-hoc record. E.g. what happens if we allow splicing in more positions?

  • The name is idiotic! Quasiquotation is so called because the quote can be escaped with an unquote. We have that already with regular TH quotes and splices. Quasiquoters don't have an unquote/splice construction so they are less "quasi" than the baseline. It's madness! The only explanation I've heard is Perl and other languages made the mistake first, so at least we're following some precedent albeit a rotten one.

@isovector
Copy link
Contributor

@simonpj do you have any further concerns about this proposal? If not, I'll be happy to make the desired changes to the proposal text --- namely the double-bar syntax and that TQuasiQuoter as may have constraints on a that participate in usual typechecking machinery both at their definition and splice sites.

@simonpj
Copy link
Contributor

simonpj commented Jul 26, 2019

and that TQuasiQuoter as may have constraints on a that participate in usual typechecking machinery both at their definition and splice sites.

I couldn't parse that sentence. Perhaps you can make the changes an point us all at the relevant bit?

But generally, yes ok.

@goldfirere
Copy link
Contributor

goldfirere commented Jul 26, 2019

I think this discussion is very interesting. If we didn't have back-compat to worry about, I would probably advocate strongly for a new, better string-literal syntax (allowing multiline literals without escapes) and to remove quasiquotes. (Another infelicity about quasiquotes: they're much more like splices than quotes, yet both the name and syntax make them look like quotes.)

Of course, we do have back-compat to worry about, and so I don't think I would do this. And, on balance, I support this proposal as it fills out a missing box. But it's good to know that almost the only reason to have quasiquotes is because we lack a better string-literal syntax, something I don't think I would have jumped to.

@reminders-prs reminders-prs bot removed the reminder label Jul 29, 2019
@reminders-prs
Copy link

reminders-prs bot commented Jul 29, 2019

👋 @isovector, accept this

@isovector
Copy link
Contributor

I've updated the proposal here: https://github.com/ghc-proposals/ghc-proposals/blob/eeeee74fdfd745a7906d237be112715323a7f887/proposals/0000-type-annotated-quoters.md . Can't for the life of me figure out how to merge it into this PR. I intend to merge this into master on Wednesday, July 31 if nobody has any complaints with my rewording.

/remind me to wants to accept this on July 31

@reminders-prs
Copy link

reminders-prs bot commented Jul 29, 2019

@isovector set a reminder for Jul 31st 2019

@reminders-prs reminders-prs bot removed the reminder label Jul 29, 2019
@isovector
Copy link
Contributor

@nomeata looks like we've reached consensus here and are ready to accept!

@nomeata nomeata merged commit b808b60 into ghc-proposals:master Aug 1, 2019
nomeata added a commit that referenced this pull request Aug 1, 2019
and convert to .rst
@nomeata
Copy link
Contributor

nomeata commented Aug 1, 2019

@nomeata looks like we've reached consensus here and are ready to accept!

Done. Would you care to also notify the mailing list? NVM, you already did :-)

@nomeata nomeata added Accepted The committee has decided to accept the proposal and removed Pending committee review The committee needs to evaluate the proposal and make a decision labels Aug 1, 2019
@Ericson2314
Copy link
Contributor

FWIW I wrote #260 because while I approve of this proposal in the short term, I would like to see QuasiQuotes deprecated in the long term.

@simonpj simonpj mentioned this pull request Jun 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Accepted The committee has decided to accept the proposal
Development

Successfully merging this pull request may close these issues.

None yet