<div dir="ltr">I had not considered the issue about incremental compilation. It certainly feels like it should cause problems. I wanted to confirm your suspicious, so I threw together an example project which is meant to cause the issues that you mentioned, but I was unable to produce any sort of error. I've thrown the code up at <a href="https://github.com/ethanpailes/th-ioref">https://github.com/ethanpailes/th-ioref</a> if you want to take a look. Basically there is a Ref module which makes an IORef, then a Quote module which reads it and a Lib module which uses the Quote module to add a top level value which depends on the contents of the IORef. I tried building it, then just modifying Quote and rebuilding. I was expecting exactly the sort of error you described, but everything works fine. When I added an executable which depends on the library, that also works fine. I feel like there should be an error though, so I would appreciate it if you could take a look at the code and try to figure out how to produce the sort of error that it seems like should occur. If it turns out that this does actually work, it would be great to know why because your point really seems like it should be right.<div><br></div><div>> <span style="font-size:12.8px">A third approach to consider is to just write out a file for each module, listing the metadata. When compiling other modules, you can use `reifyModules` to get the imports, so that you know which metadata files to load. One issue with this (as well as the IORef) is that it won't work across multiple packages.</span></div><div><span style="font-size:12.8px"><br></span></div><div><span style="font-size:12.8px">Do you know if there is a way to learn where the build output is going in the Q Monad? It would be much better to write the tmp files out to some place in .stack_work rather than in /tmp. That way if /tmp disappears we don't loose the cached data.<br></span><div><div><br></div><div>Ethan</div></div></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Mar 10, 2017 at 5:47 PM, Michael Sloan <span dir="ltr"><<a href="mailto:mgsloan@gmail.com" target="_blank">mgsloan@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>Hi!<br><br>I don't think the IORef approach will work well, because GHC won't recompile modules unless they've changed (or if a file added via qAddDependentFile changes). So if you've done a compile of module A, and B depends on it, and then you just change B, only B will recompile and you won't have info from A.</div><div><br></div><div>Not sure if this applies to your circumstance, but one issue with the top level declaration approach is that TH can't enumerate the members of a module. One hacky way to write down such metadata in a retrievable way is to use typeclass instances. For example you might have</div><div><br></div><div>class TypeInfo (name :: Symbol) (typeInfo :: Symbol)</div><div><br></div><div>Then, you can reify the typeclass to get all its instances. As long as you only need to write out this info from declaration quasiquotes, this info ought to be available for typechecking later in the module.</div><div><br></div><div>A third approach to consider is to just write out a file for each module, listing the metadata. When compiling other modules, you can use `reifyModules` to get the imports, so that you know which metadata files to load. One issue with this (as well as the IORef) is that it won't work across multiple packages.</div><div><br></div><div>-Michael</div><div><div class="h5"><div><br>On Fri, Mar 10, 2017 at 12:29 PM Ethan Pailes <<a href="mailto:ethanpailes@gmail.com" target="_blank">ethanpailes@gmail.com</a>> wrote:<br></div></div></div><div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5"><div class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:12.8px" class="m_7436650558945940356m_-3012118238692806727gmail_msg">Hi,</span><div style="font-size:12.8px" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><br class="m_7436650558945940356m_-3012118238692806727gmail_msg"></div><div style="font-size:12.8px" class="m_7436650558945940356m_-3012118238692806727gmail_msg">I've been working on PADS with Kathleen Fisher for the past couple of months, and we have some questions about the Q Monad.</div><div style="font-size:12.8px" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span id="m_7436650558945940356m_-3012118238692806727m_3743956778848969250gmail-m_-3057074410276659965gmail-docs-internal-guid-a45c5dc5-b9d2-3414-c9ad-5bad62b8bb51" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><br class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">In working on extending the PADS language, we have encountered a need to maintain a type environment across invocations of the [pads||] quasi quoter. This allows PADS types to be defined in one quote block and used in a later quote block. Unfortunately, the right way to do this is not obvious from the Q Monad’s interface[1]. The monad provides `qGetQ` and `qPutQ` methods to provide a place to store state across quotes, but unfortunately this state is local to each module. It is desirable from a usability perspective to be able to reuse PADS declarations across modules, but in the particular case of PADS the problem is actually worse than that. PADS ships with a standard library module which uses PADS to define several of the core types required to be productive, so without the ability to do type checking across modules the use of this standard library becomes impossible. There are two solutions to the problem that we have considered.</span></p><br class="m_7436650558945940356m_-3012118238692806727gmail_msg"><ol style="margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><li style="margin-left:15px;list-style-type:decimal;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">We can generate metadata about each type as it is defined and emit a top level declaration which contains this metadata.</span></p></li><ol style="margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><li style="margin-left:15px;list-style-type:lower-alpha;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">This clutters the global namespace.</span></p></li><li style="margin-left:15px;list-style-type:lower-alpha;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">It lets you examine the metadata for each type easily in `ghci` which leads to a nice debugging workflow.</span></p></li><li style="margin-left:15px;list-style-type:lower-alpha;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">If you want to perform any typechecks or other calculations based on the metadata, they must happen after generated code is spliced in.</span></p></li><ol style="margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><li style="margin-left:15px;list-style-type:lower-roman;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">This means you lose a lot of type safety because everything must be represented as an `Exp` (even with typed template haskell there is no way to get a type for an `Exp` of the form `VarE <<name of metadata>>`)</span></p></li><li style="margin-left:15px;list-style-type:lower-roman;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">It becomes impossible to report type errors at compile time. This is a dealbreaker because otherwise, what is the point?</span></p></li></ol></ol><li style="margin-left:15px;list-style-type:decimal;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">Using `unsafePerformIO` to create an IORef and then using the Q Monand’s `qRunIO` function to access the state cell.</span></p></li><ol style="margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><li style="margin-left:15px;list-style-type:lower-alpha;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">This works smoothly across modules</span></p></li><li style="margin-left:15px;list-style-type:lower-alpha;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">You get type safety back because everything happens in the Q Monad. You don’t have to generate code in order to get at the values.</span></p></li><li style="margin-left:15px;list-style-type:lower-alpha;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">It uses `unsafePerformIO` and `qRunIO` so you have to worry about how often everything gets run.</span></p></li><ol style="margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><li style="margin-left:15px;list-style-type:lower-roman;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">It seems like it should be safe with a {-# NONINLINE #-} pragma for the code which creates the IORef.</span></p></li><li style="margin-left:15px;list-style-type:lower-roman;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">The IORef is hidden, and no one else should be able to modify it.</span></p></li><li style="margin-left:15px;list-style-type:lower-roman;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">Adding stuff to a `Data.Map.Strict` (the environment implementation we are using) is idempotent, so the quote code getting run multiple times should not be an issue.</span></p></li></ol></ol></ol><br class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">We have two main questions based on this.</span></p><ol style="margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><li style="margin-left:15px;list-style-type:decimal;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">Is the IO (ab)use outlined above really as safe as we think it is?</span></p></li><li style="margin-left:15px;list-style-type:decimal;font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">Given that environments are such a common requirement in compiling programming languages, and the goal of quasi quotation is allowing easy creation of EDSLs for Haskell, does it make sense to provide some state mechanism for the Q monad which is not restricted by modules.</span></p></li></ol><br class="m_7436650558945940356m_-3012118238692806727gmail_msg"><br class="m_7436650558945940356m_-3012118238692806727gmail_msg"><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">[1]: <a href="https://hackage.haskell.org/package/template-haskell-2.11.1.0/docs/Language-Haskell-TH-Syntax.html" class="m_7436650558945940356m_-3012118238692806727gmail_msg" target="_blank">https://hackage.haskell.org/pa<wbr>ckage/template-haskell-2.11.1.<wbr>0/docs/Language-Haskell-TH-Syn<wbr>tax.html</a></span></p><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><br class="m_7436650558945940356m_-3012118238692806727gmail_msg"></span></p><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">Thanks,</span></p><p style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" class="m_7436650558945940356m_-3012118238692806727gmail_msg"><span style="font-size:11pt;font-family:arial;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap" class="m_7436650558945940356m_-3012118238692806727gmail_msg">Ethan Pailes</span></p></span></div></div></div></div>
______________________________<wbr>_________________<br class="m_7436650558945940356m_-3012118238692806727gmail_msg">
Libraries mailing list<br class="m_7436650558945940356m_-3012118238692806727gmail_msg">
<a href="mailto:Libraries@haskell.org" class="m_7436650558945940356m_-3012118238692806727gmail_msg" target="_blank">Libraries@haskell.org</a><br class="m_7436650558945940356m_-3012118238692806727gmail_msg">
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries" rel="noreferrer" class="m_7436650558945940356m_-3012118238692806727gmail_msg" target="_blank">http://mail.haskell.org/cgi-bi<wbr>n/mailman/listinfo/libraries</a><br class="m_7436650558945940356m_-3012118238692806727gmail_msg">
</blockquote></div></div></div>
</blockquote></div><br></div>