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
Proposal: get rid of wrapper packages #2106
Comments
@kosmikus @adinapoli if you have any thoughts, they will be appreciated. |
Thanks @facundominguez - I think this is very important! A couple of things
CURRENTLY in LH we have a way to retroactively slap on specs to package- and module- external functions using the “assume” pragma module Moo where LJ
which says, (if foo is not defined in the module Moo where the above appears), to use T as the signature for foo in that module Moo (only). IIRC (we can check the code or maybe @adinapoli can recall) these assume are NOT serialized with the module annotations and hence NOT exported by Moo. Perhaps the solution is to have an module Moo where
such that also tells any client of Moo to assume foo :: T If we do so, then the current liquid-X, packages could be refactored so they only “export” the relevant signature instead of “assume” ing it? |
If the refactoring implies that liquid-X package doesn't need to redefine the modules in the original package X, it looks like a step forward. Now we would need to define how a program does bring the exported assumptions into scope. These could be by importing the module with a regular import declaration: import My.Assumptions.Module() or by using an import directive, {-@ import My.Assumptions.Module @-} when we want to state that a module import is only needed for verification. Then we need to decide on whether to add a mechanism to reexport assumptions from imports.
👍 I edited this in the description.
This makes sense to me, so the user continues to use only one tool to express dependencies between packages. |
I also agree with @facundominguez's suggestion to get rid of the wrapper packages, for three reasons:
My understanding is that the current solution is close to what we had before, i.e., Define and export I believe this seems cleaner. |
If we do use So, currently, my suggestion is to 1) define the type signatures in spec files and 2) use annotations to import them. |
In that case, perhaps we can use .spec files for boot libraries. However, I think spec files won't solve the problem for other libraries. For instance, suppose we have LH assumptions about functions in package The options are either putting the assumptions in the |
Import annotations can tell which spec file is needed, but I don't think they can say where in the system the file is located. |
The following assumption about
Importing B in module C is necessary for verification to succeed. I couldn't find any differences in behavior when putting the assumption in a .spec file. The key change determining if the assumption is exported or not, is whether the identifier in the If I introduce conflicting assumptions about |
I agree that the problem is where to put the annotations. Ideally we want them in the package (e.g., |
While investigating solutions, I realized that there is a design choice to make. The problem is to bring assumptions into scope that are not provided in the imports of the module being verified.
The answer to this question could be different depending on whether we are talking about boot libraries, or whether we are talking about other libraries of which LH is unaware at build time. I like option (2) the most because it gives the user the most control with the simplest means. We still will need to have Option (1) doesn't require manually importing assumptions, but it has the risk of bloating the environment with unnecessary assumptions and measures, and it could introduce disrupting assumptions too. Moreover, any mechanism to import assumptions implicitly seems to impose some burden on authors of assumptions as in the following explanation. In order to decide which assumptions to bring into scope, LH would look at the import declarations of the module being verified. Suppose there is an import declaration like With respect to the current situation, both options save the trouble of editing the cabal configuration to replace Only option (2) saves the trouble of dealing with changes in the module hierarchy across versions of a dependency that needs assumptions. Any preferences? |
I prefer (1) at least for the "prelude" and similar because without implicitly loading the assumptions for stuff like Perhaps the solution is some mixture. So
|
I started moving spec files from Today I was struck by another approach: merge the assumptions of the Users of lh need only depend on The advantage of this approach is that it could be reused for boot and non-boot libraries. Whenever a module imports module Thoughts welcome. |
Problem
Verifying a program with liquid haskell requires one to state the assumptions about how dependencies work. The notable exception is when a dependency is also verified with LH, in which case it will contain specifications that can be used to verify the program.
The current mechanism to manage assumptions is to create a Haskell package that states the assumptions (e.g.
liquid-base
) and then have the program use it instead of the actual package (e.g.base
). In order to state the assumptions, Liquid Haskell requires theliquid-base
to redefine all the modules ofbase
on which assumptions are needed and reexport all of the other modules. This is a hassle because users need to:liquid-*
packages for every dependency they intend to make assumptions about.A solution
I propose that we introduce some changes to LH so we don't need wrapper packages anymore.
A first change is to give LH a mechanism to state reusable assumptions about dependencies without having to redefine Haskell modules only for that sake. In this way, if we want to throw all of the assumptions about a dependency into a single module, we can do it, and moreover, we can do it without having to change the dependency of the program. Making the assumptions reusable means that we can somehow point LH to the module containing the assumptions, and LH will be able to use them in other places.
A second change is to pack inside the LH plugin all of the essential assumptions made for the boot libraries. These libraries depend on the GHC version used, and there is no reason to ask the user to manage the assumptions about boot libraries separately from enabling the LH plugin.
A last change is to allow to configure the LH plugin so it knows where to find the assumptions about dependencies when they are not boot libraries. There are a few options here:
{-@ import My.Liquid.Assumptions @-}
Liquid_Dependency_Assumptions
and its imports.M
in some other moduleLiquid.Assumptions.M
.It would be helpful to collect others thoughts on this matter as I'm finding tricky to navigate the design space. Also, I'm not too knowledgeable on how the current LH works, so perhaps there are already solutions which are similar to the proposed changes.
The text was updated successfully, but these errors were encountered: