Skip to content

A "safe" subsection of the library #143

Description

@MatthewDaggitt

As discussed in #122 it is currently impossible to typecheck a development relying on the standard library with the --safe option because it does not isolate properly its uses of unsafe features.

The agreed upon course of action is that there will be a Safe.* subset of modules. The current module names will be kept the same and will re-export exactly what they currently contain, ensuring full backwards compatibility whilst making it possible to only rely on the subset that is recognised as being safe by Agda.

Activity

  1. gallais commented on Aug 1, 2017

    @gallais
    Member

    I have come across something fairly nasty: Agda.Builtin.Int depends upon Agda.Builtin.String which uses a postulate to declare the String type. As a consequence any module importing Agda.Builtin.Int cannot be accepted by the current --safe option Agda has.

    In other words either all Data.Integer.* and any other module depending upon them have to be in the unsafe part of the library or Agda needs to provide e.g. Safe.Agda.Builtin.Int without primShowInteger (and therefore without any dependence on Agda.Builtin.String).

  2. added a commit that references this issue on Aug 1, 2017
  3. UlfNorell commented on Aug 2, 2017

    @UlfNorell
    Member

    It sounds like maybe we should relax the requirements of --safe to accept postulates of type Set.

  4. gallais commented on Aug 2, 2017

    @gallais
    Member

    I don't understand why Agda.Primitive doesn't trigger an error given that it does use postulate all over the place.

  5. gallais commented on Aug 3, 2017

    @gallais
    Member

    All the tests are green. I think the branch is more or less ready to be merged (and I'd like to avoid it diverging too much from master before merge because that's a lot of changes to maintain!). When / If Agda gets more liberal in terms of what it accepts as safe, we can move more things into Safe/. It should all be transparent from the users' point of view anyway.

    The main issues I see are:

    Wider Use

    Do we want to ask people on the agda ML to try to check their developments with the current safe branch to make sure it doesn't break anything (e.g. we haven't forgotten to re-export any names anywhere)?

    Or would it be enough to run Agda's stdlib-based test suite on the modified one? In which case I guess it's simply a matter of creating a dummy branch on agda/agda where we update the submodule to point to the safe branch.

    Stylistic Issue

    Do we want {-# OPTIONS --safe #-} as a header in all of the Safe/ modules or are we just happy with having travis check Safe/index.agda with the appropriate command line options? At the moment we have both the header and the travis separate check (the latter is necessary even though we have the headers because of agda/agda#2487).

  6. MatthewDaggitt commented on Aug 3, 2017

    @MatthewDaggitt
    CollaboratorAuthor

    Just had a look at the branch. What's the plan for maintaining this? Is every safe change to the non-safe part going to have to be copied over to the safe part manually?

    Or is there some way of regenerating the safe part automatically?

  7. gallais commented on Aug 3, 2017

    @gallais
    Member

    The idea is to replace master with safe. Given that each unsafe module re-exports the content of its safe counterpart, it should be completely transparent for end users.

    New definitions can be added either to the safe module if they are innocuous, or to the unsafe one if they're not. Data.Nat.Base is a pretty good example: pretty much all of its former content is now in Safe.Data.Nat.Base except for erase which is in Data.Nat.Base.

  8. MatthewDaggitt commented on Aug 3, 2017

    @MatthewDaggitt
    CollaboratorAuthor

    Oh yes. Apologies, hadn't noticed that all the non-safe versions contents had been moved.

    Given that each unsafe module re-exports the content of its safe counterpart, it should be completely transparent for end users.

    Until they go browsing through it!

    I'm a little worried about what this is going to do to library usability. For instance looking for the decidability of the divides relation _∣_, would currently only involve looking through Data.Nat.Divisibility. However it is now going to require browsing both Safe.Data.Nat.Divisibility and Data.Nat.Divisibility which are deep in different parts of the heirarchy. What's more you can't off-hand predict which one it's going to belong to.

    This seems like a big impact on the average user who's not that concerned about rigorous safety. Can users who are concerned about safety not just type-check it on the command line using the --safe option? Won't that automatically highlight the unsafe terms used? Or does it over-estimate, and mark otherwise safe terms unsafe if they share a file with unsafe terms?

  9. gallais commented on Aug 3, 2017

    @gallais
    Member

    However it is now going to require browsing both Safe.Data.Nat.Divisibility and Data.Nat.Divisibility which are deep in different parts of the heirarchy. What's more you can't off-hand predict which one it's going to belong to.

    The doc for the unsafe module systematically links to the safe one because it imports it.

    Can users who are concerned about safety not just type-check it on the command line using the --safe option?

    --safe only succeeds if all of the definitions in a module (and all the module it depends upon) are safe. Hence the need for a split: it is currently near impossible to typecheck a development relying on the standard library with the --safe option. I'm actually surprised that after 100+ articles using Agda published, no reviewer seems to have complained about this.

  10. MatthewDaggitt commented on Aug 3, 2017

    @MatthewDaggitt
    CollaboratorAuthor

    The doc for the unsafe module systematically links to the safe one because it imports it.

    Hmm... I guess so.

    --safe only succeeds if all of the definitions in a module (and all the module it depends upon) are safe.

    Whereas what it should ideally do is only check the definitions in other modules that are actually used. From my position of complete ignorance, this seems like it should be doable using some sort of flow analysis. I'm not sure if it is or not! If it is possible, wouldn't this be a far better solution than partitioning the standard library in two?

    I guess its kind of the dual to agda/agda#2487. File options should be more strict with respect to imported definitions, and command line options need to be less strict....

  11. gallais commented on Aug 3, 2017

    @gallais
    Member

    Whereas what it should ideally do is only check the definitions in other modules that are actually used.

    But then you'd have to call agda --safe on every single module attached to a paper to make sure the whole development is not using any dirty tricks. Right now you can simply generate an Everything.agda and typecheck that.

    Edit: I should perhaps mention the existence of C-c C-z to search through definitions in scope. Which ought to be more efficient than navigating files when looking for a specific result.

  12. MatthewDaggitt commented on Aug 3, 2017

    @MatthewDaggitt
    CollaboratorAuthor

    But then you'd have to call agda --safe on every single module attached to a paper to make sure the whole development is not using any dirty tricks

    Aren't you going to have to do that anyway? Even if you've only used modules from the Safe hierarchy in the standard library, you still need to check that your own modules don't use any dirty tricks.

  13. gallais commented on Aug 3, 2017

    @gallais
    Member

    You can call agda --safe on a module Everything.agda importing all the modules in your development and it'll make sure everything is a-ok. Just like the travis test at the moment checks the whole of Safe/ simply by checking Safe/index.agda.

  14. 13 remaining items

  15. asr commented on Apr 11, 2018

    @asr
    Member

    Why doesn't the command:

    agda --safe src/Relation/Binary/HeterogeneousEquality/Quotients.agda
    

    throw an error?

    When trying of reproducing the problem in Agda stable-2.5 branch I got the error:

    $ make up-to-date-std-lib
    ...
    cabal exec -- GenerateEverything
    GenerateEverything: src/Relation/Binary/HeterogeneousEquality/Quotients.agda is malformed.
    CallStack (from HasCallStack):
      error, called at GenerateEverything.hs:67:15 in main:Main
    GNUmakefile:16: recipe for target 'Everything.agda' failed
    
  16. MatthewDaggitt commented on Apr 11, 2018

    @MatthewDaggitt
    CollaboratorAuthor

    Hmm weird. I'm on a clean version of 2.5.3. I'm having the same problem with the --without-K option...

  17. MatthewDaggitt commented on Apr 12, 2018

    @MatthewDaggitt
    CollaboratorAuthor

    Tracked it down, it was another subtler instance of agda/agda#2487. As I'd compiled the whole library without --safe, the .agdai files were lying to me when I subsequently recompiled it all with --safe. I'm rapidly coming to the opinion that the --safe option currently doesn't provide anything much in the way of guarantees (either in-file or at the command line) 😞

  18. gallais commented on Apr 12, 2018

    @gallais
    Member

    --safe + --ignore-interfaces is pretty reliable

  19. WolframKahl commented on Apr 12, 2018

    @WolframKahl
    Member
  20. nad commented on Apr 13, 2018

    @nad
    Contributor

    I'm rapidly coming to the opinion that the --safe option currently doesn't provide anything much in the way of guarantees (either in-file or at the command line)

    The --safe feature was originally implemented by someone who is no longer contributing to Agda, and it seems as if no regular developer is eager to maintain it. Fixing the documented bugs related to --safe requires some work, but I think it should be doable for someone who is not intimately familiar with the internals of Agda. Contributions are welcome.

  21. MatthewDaggitt commented on Apr 13, 2018

    @MatthewDaggitt
    CollaboratorAuthor

    I'm rapidly coming to the opinion that the --safe option currently doesn't provide anything much in the way of guarantees (either in-file or at the command line)

    Fixing the documented bugs related to --safe requires some work, but I think it should be doable for someone who is not intimately familiar with the internals of Agda. Contributions are welcome.

    While I would love to, I feel I already spend far too much of my time of my PhD on internet routing protocols playing around with Agda! Sorry, I realise that my original comment perhaps came off as a complaint, it wasn't meant to be.

  22. MatthewDaggitt commented on Apr 13, 2018

    @MatthewDaggitt
    CollaboratorAuthor

    I've started working on creating the various Unsafe modules (very similar to @gallais work on #122). It's all mostly okay (although haven't got to Data.Integer yet...) but the first problem I've come across is in Data.Nat.DivMod.

    record DivMod (dividend divisor : ℕ) : Set where
    constructor result
    field
    quotient : ℕ
    remainder : Fin divisor
    property : dividend ≡ toℕ remainder + quotient * divisor

    The problem is that the remainder field is of type Fin divisor and not ℕ. For efficiency's sake (although apparently it doesn't work very well, see #150) we erase all the various proof that allows us to convert from ℕ to Fin divisor. The erasure uses trustMe which is, of course, unsafe. This then pollutes large parts of the following modules that depend on DivMod:

    Data.Nat.Divisibility
    Data.Nat.Primality
    Data.Nat.Show
    Data.Digit
    

    There are three options that I see:

    1. Proceed as planned and stick all dependencies in Unsafe modules.
      • Advantages: A little more backwards compatible (although obviously all module imports will have to change).
      • Disadvantages: we label large sections of our Nat library as Unsafe, including our primality test which is a tad embarrassing.
    2. Change the type of _mod_ and the field remainder to ℕ rather than Fin divisor, and then create separate proofs that show they are all related in the right way.
      • Advantages: This would provide a faster and safe version of _mod_, and hence avoid polluting the safety of the rest of the library with the use of trustme. The proofs that the right properties hold, still exist, and users themselves can make the choice of whether or not to erase them for efficiency reasons.
      • Disadvantages: not backwards compatible
    3. Simply remove the erase calls:
      • Advantages: Completely backwards compatible and all the files automatically become safe.
      • Disadvantages: Performance gets even worse.

    I'd prefer the 2nd option as it feels much more principled. Not all users will require the proofs, but those that do then have much finer grained control over how they use them. The non-backwards compatability is unfortunate but that's why we're labelling it v1.0?

  23. HuStmpHrrr commented on Apr 14, 2018

    @HuStmpHrrr
    Member

    Disadvantages: Performance gets even worse.

    I am astonished by the performance part. Might I ask how important performance is, and how many people are relying on it? Sorry about my ignorance...

    What about irrelevance? I've not seen the description in official doc, but from my understanding, this should be the secrete magic to tame the tradeoff between complex proof flow and performance, is it not?

  24. MatthewDaggitt commented on Apr 16, 2018

    @MatthewDaggitt
    CollaboratorAuthor

    I am astonished by the performance part. Might I ask how important performance is, and how many people are relying on it? Sorry about my ignorance...

    No idea. I don't personally normalise any of my proofs. But given that Ulf has put significant effort into building agda-prelude with a strong focus on performance I gather it's definitely important for some people. Also issue #150 is is actually directly related to the performance of this part of the library.

    What about irrelevance? I've not seen the description in official doc, but from my understanding, this should be the secrete magic to tame the tradeoff between complex proof flow and performance, is it not?

    Again, as I never worry about actually normalising I've never had any need to dip into irrelevance. Maybe someone with more experience could chime in?

    Overall, I think the consensus is that the standard library focuses on making proofs easier rather than on performance. Having said that, we should probably generally avoid making performance worse except for good reason. Hopefully we can come to some conclusion here whether or not safety is a good enough reason!

  25. MatthewDaggitt commented on Jul 6, 2018

    @MatthewDaggitt
    CollaboratorAuthor

    #350 which has just been merged provides a way forward on this problem. It adds a new "safe" modular arithmetic library. The old library can then be placed in the Unsafe module. The dependencies have been updated to use the new library.

  26. added this to the 1.0 milestone on Aug 30, 2018
  27. MatthewDaggitt commented on Aug 30, 2018

    @MatthewDaggitt
    CollaboratorAuthor

    Closed with 8885d7a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions