Q Monad State

Ethan Pailes ethanpailes at gmail.com
Sat Mar 11 02:22:04 UTC 2017


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 https://github.com/ethanpailes/th-ioref 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.

> 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.

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.

Ethan

On Fri, Mar 10, 2017 at 5:47 PM, Michael Sloan <mgsloan at gmail.com> wrote:

> Hi!
>
> 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.
>
> 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
>
> class TypeInfo (name :: Symbol) (typeInfo :: Symbol)
>
> 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.
>
> 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.
>
> -Michael
>
> On Fri, Mar 10, 2017 at 12:29 PM Ethan Pailes <ethanpailes at gmail.com>
> wrote:
>
>> Hi,
>>
>> I've been working on PADS with Kathleen Fisher for the past couple of
>> months, and we have some questions about the Q Monad.
>>
>> 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.
>>
>>
>>    1.
>>
>>    We can generate metadata about each type as it is defined and emit a
>>    top level declaration which contains this metadata.
>>    1.
>>
>>       This clutters the global namespace.
>>       2.
>>
>>       It lets you examine the metadata for each type easily in `ghci`
>>       which leads to a nice debugging workflow.
>>       3.
>>
>>       If you want to perform any typechecks or other calculations based
>>       on the metadata, they must happen after generated code is spliced in.
>>       1.
>>
>>          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>>`)
>>          2.
>>
>>          It becomes impossible to report type errors at compile time.
>>          This is a dealbreaker because otherwise, what is the point?
>>          2.
>>
>>    Using `unsafePerformIO` to create an IORef and then using the Q
>>    Monand’s `qRunIO` function to access the state cell.
>>    1.
>>
>>       This works smoothly across modules
>>       2.
>>
>>       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.
>>       3.
>>
>>       It uses `unsafePerformIO` and `qRunIO` so you have to worry about
>>       how often everything gets run.
>>       1.
>>
>>          It seems like it should be safe with a {-# NONINLINE #-} pragma
>>          for the code which creates the IORef.
>>          2.
>>
>>          The IORef is hidden, and no one else should be able to modify
>>          it.
>>          3.
>>
>>          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.
>>
>>
>> We have two main questions based on this.
>>
>>    1.
>>
>>    Is the IO (ab)use outlined above really as safe as we think it is?
>>    2.
>>
>>    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.
>>
>>
>>
>> [1]: https://hackage.haskell.org/package/template-haskell-2.11.1.
>> 0/docs/Language-Haskell-TH-Syntax.html
>>
>>
>> Thanks,
>>
>> Ethan Pailes
>> _______________________________________________
>> Libraries mailing list
>> Libraries at haskell.org
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/libraries/attachments/20170310/4720151f/attachment-0001.html>


More information about the Libraries mailing list