-
Notifications
You must be signed in to change notification settings - Fork 32
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
A few propositions about the module system #58
base: master
Are you sure you want to change the base?
Conversation
## Ideas inherited from OCaml's module system | ||
|
||
OCaml has two module system features which Coq does not have: | ||
- Private inductive types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we have private inductive types in coq?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it does not mean the same and it's not a kernel feature (read: it's a broken hack).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ocaml private
is used to enforce guarantees about the values in the type.
In Coq this is can done by explicitly manipulating proofs about the values in the type, so there seems to be no need to for ocaml-style private
.
Also I'm not sure how it would work, consider
Module Import SortedList.
Private(ocaml) Inductive t := Nil | Cons : nat -> t -> t.
Definition nil := Nil.
Definition cons x l := (* add x to l keeping it sorted *).
End SortedList.
Compute nil. (* Nil : t ??? *)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose opaque types would have abstract values, as in OCaml: <abstract> : t
.
- Private inductive types | ||
- Axiomatic variant of `Module Type` providing a polymorphically-typed module system | ||
|
||
## Providing more algebraic combinators to build theories |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you got any more combinators in mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really. I was mostly thinking at the oppportunity to integrate Include
and the quite powerful <+
in the kernel.
In Why3, we do not distinguish between module implementations and module interfaces. For instance, your following example works fine once translated to WhyML: Module M. Axiom a:nat. End M.
Module N := M with Definition a:=0.
The system works rather well, including private types and extraction. So, perhaps, some ideas could be reused for Coq. That said, Why3 has a much simpler type system, so hard to tell how bad it would behave if Coq's conversion was thrown into the mix. Some details are given in this paper: https://hal.inria.fr/hal-02696246 |
The simplicity of the module system of Why3 is appealing (and somehow "to the point"). If I understand correctly, it can emulate higher-order functors, |
By the way, shouldn't your example (morally) use Why3's |
Yes. As for
No. As in Coq and OCaml, Why3's modules act both for namespacing and as a higher-order type system. Directive But you are right that it causes an issue if you want inductive types to be shared (since module cloning is generative). In that case, you would use the same trick as in Coq (which makes it just as tedious):
Now, any clone of |
|
||
OCaml has two module system features which Coq does not have: | ||
- Private inductive types | ||
- Axiomatic variant of `Module Type` providing a polymorphically-typed module system |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this lead to a type-in-type inconsistency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this proposal be clarified? Is this Declare Module Type Foo.
where Foo
is then an abstract signature?
|
||
In the implementation, these limitations come from the difference between `MExpr(params,expr,None)` (= a module with interface canonically identified with the implementation) and `MType(params,expr)` (= a module with implementation canonically identified with the interface). This difference goes to `module_body` by giving either the value `FullStruct` or the value `Abstract` to the field `mod_expr`. | ||
|
||
Both issues would be solved by identifying the two constructions (i.e. rebinding `MType(params,expr)` to `MExpr(params,expr,None)`) and identifying `FullStruct` and `Abstract`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@herbelin Are you proposing mixin modules, a bit like in Scala or MixML? I’ve often wanted them, and I’ve actually spent a couple of years studying the Scala case.
Here’s a small example:
Module Type Base.
Axiom a : nat.
Definition b := 1 + a.
End Base.
Module Derived1 <: Base.
Definition a : nat := 0.
Include Base.
(* Include should _merge_ the axiom `a` with the `Definition `` as long as the definition is a subtype of the interface — if Coq satisfies the usual metatheory of subtyping, definitions using `a` should still typecheck and need not be rechecked, except for universe constraints since those are not modular. *)
End Derived1.
Module Derived2 <: Base.
Include Base.
Fail Definition a : nat := b. (* This creates a new kind of infinite loop, which must be forbidden. *)
Definition a : nat := 0.
(* Such things work in Scala but is harder, since we have to _replace_ a definition; rejecting this might be fine here, but not so well with FancyBase below. To make this work, we could replace [Include] by some other “inheritance” mechanism. *)
End Derived2.
Module FancyBase.
Include Base.
Axiom c : Fin b.
Definition d := (c, c).
End FancyBase.
Module FancyDerived1 <: FancyBase.
Definition a : nat := 0.
Definition c : Fin 1 := Fin.FZ. (* We cannot write [Fin b], and we might be unable to write this definition at all. *)
Include FancyBase.
End FancyDerived1.
Module FancyDerived2 <: Base.
Include FancyBase.
(* Now we can fill in the axioms *)
Definition a : nat := 0.
Definition c : Fin b := Fin.FZ.
End FancyDerived2. (* *)
This code can be translated to avoid mixing modules — Coq’s numerical hierarchy shows how. But the encoding is pretty annoying, since you can never mix axioms and definitions if the axioms are to be later instantiated.
I also realize that working out the details is not trivial; but I am not sure what you get if you stop short of mixin modules.
This CEP discusses a few limitations of the module system and give propositions to address them.
Rendered here