Opened 4 years ago

Last modified 4 years ago

#11584 new bug

[Template Haskell] Language.Haskell.TH.Syntax.hs contains misleading comment

Reported by: bollmann Owned by: bollmann
Priority: normal Milestone:
Component: Template Haskell Version: 7.10.3
Keywords: newcomer Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Documentation bug Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description

In Language.Haskell.TH.Syntax it reads on the generation of fresh names:

Although names generated by @newName@ cannot /be captured/, they can
/capture/ other names. For example, this:

>g = $(do
>  nm1 <- newName "x"
>  let nm2 = mkName "x"
>  return (LamE [VarP nm2] (LamE [VarP nm1] (VarE nm2)))
> )

will produce the splice

>g = \x -> \x0 -> x0

since the occurrence @VarE nm2@ is captured by the innermost binding
of @x@, namely @VarP nm1@.

I find this note quite misleading, especially since the above splice normally generates something like

\ x -> \ x_a3vv -> x

where no inadvertent capture occurs.

I belive this note should rather be something as follows:

Although names generated by @newName@ cannot /be captured/, they can
/capture/ other names. For example, suppose that the next invocation
of newName would generate the name nm1 = x0, and we have the
following splice, where mkName uses the very same name x0 for
nm2:

>g = $(do
>  nm1 <- newName "x"
>  let nm2 = mkName "x0"
>  return (LamE [VarP nm2] (LamE [VarP nm1] (VarE nm2)))
> )

This will produce the following splice, where nm1 generated by newName
captures nm2 assigned by mkName:

>g = \x0 -> \x0 -> x0

In particular, now the occurrence @VarE nm2@ is captured by @VarP nm1@
and not by @VarP nm2" anymore (as was intended by the TH AST).

I'm happy to change this, if people agree :-).

Change History (7)

comment:1 Changed 4 years ago by goldfire

That comment seems utterly wrong to me. Can you observe the weird capturing behavior described? What's your example?

comment:2 Changed 4 years ago by bollmann

Yea, I thought the comment to be wrong at first glance as well, in particular since I couldn't observe this capturing behavior at all: The mentioned splice generates something like

\ x -> \ x_a3vv -> x

Which is exactly as specified in the TH AST and thus as expected.

But at 2nd reading I was thinking that the original intent of this comment might have been to explain that a newly generated Name via newName might clash with the name chosen by mkName. That is, suppose newName would generate the name x_1234 next, and by bad luck we generate the same name x_1234 by means of mkName. Then these two names might interfere and capturing results (see my revised message in the ticket description at the very end for more details).

Or is this case not possible and we should just remove this comment entirely instead of modifying it?

comment:3 Changed 4 years ago by goldfire

Without going into that code deeply, I think this comment is just wrong. My best advice is to try to reproduce the behavior it suggests. If that fails, delete the comment.

comment:4 Changed 4 years ago by rwbarton

I don't understand what you are all talking about :)

Try this complete program:

{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
g = $(do
  nm1 <- newName "x"
  let nm2 = mkName "x"
  return (LamE [VarP nm2] (LamE [VarP nm1] (VarE nm2)))
 )
main = putStrLn $ g "1" "2"

It outputs 2, as advertised by the comment in question.

I happened to be looking at this the other day for unrelated reasons. mkName "x" means "whatever variable of name x is in scope", and newName "x" returns a new variable whose name is x. It is a different variable than is returned by other calls to newName "x", but all of them capture the name x as far as mkName is concerned. As far as I can tell it's all exactly as explained in that comment.

comment:5 Changed 4 years ago by goldfire

That's surprising to me, but not so terribly surprising. That's why I wanted a test before deleting the comment!

comment:6 Changed 4 years ago by bollmann

@rwbarton: interesting. Thanks for clarifying the true nature of this comment!

I sadly hadn't run the generated splice g "1" "2" as you then suggested, but had found the comment misleading solely based on enabling the -ddump-splices flag. When enabled, the splice in your program expands to something like:

\ x -> \ x_a3uK -> x

And thus I thought, that the comment of newName capturing mkNames didn't make sense at all, since the generated splice agrees with the specified TH AST.

Hence, it seems that the splice expansion displayed by -ddump-splices does not agree with what is actually generated when running the code g "1" "2".

comment:7 Changed 4 years ago by goldfire

Ah, yes, that makes some sense. When the pretty-printer appends _xyz to an identifier, that's the identifier's Unique. TH Names sometimes lead to identifiers with Uniques (e.g., newName identifiers and identifiers from quoting) and sometimes not (mkName). In the latter case, the variable will refer to the innermost identifier with the same name, regardless of its Unique.

I agree the pretty-printer is confusing here. Maybe instead of x_a3uK, it should be x{a3uK}? It would then be clear that the underscore isn't part of the name proper. Not sure if this is better or worse.

Note: See TracTickets for help on using tickets.