Mantis Bug Tracker

View Issue Details Jump to Notes ] Issue History ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0005764OCamlOCaml generalpublic2012-09-22 19:222013-09-03 13:44
Reporterhongboz 
Assigned To 
PrioritynormalSeverityfeatureReproducibilityhave not tried
StatusacknowledgedResolutionopen 
PlatformOSOS Version
Product Version 
Target VersionFixed in Version 
Summary0005764: A way to avoid automatic export of declaration in inferred signatures
Descriptionfor example file A.ml have a lot of generated code beginning with
__ocaml__blabla, in most cases programmers don't want to expose such functions in A.mli(and it maybe dangerous), so for the automatically generated interface file, it would be nice that the default behavior should hide them, user can write A.mli by hand and expose them if they wish. This also applies generated code by ocaml(lex|yacc)
TagsNo tags attached.
Attached Files

- Relationships

-  Notes
(0008145)
hcarty (reporter)
2012-09-23 04:01

A more backwards compatible and perhaps less shocking approach could be to have 'ocamlc -i foo.ml' not include these values. This could be the default or something which could be set through a command line flag.
(0008146)
hongboz (developer)
2012-09-23 04:13

hcarty: the problem is that sometimes you don't want to write .mli file, and simply want to hide functions beginning with '__' by default. Your solution still requires people to write or generate .mli file. If user writes .mli file, they can still expose functions beginning with '__', personally, I think the benefit is immediate.
To maintain backwards compatibility, I agree that may be an compilation flag '-no_implicit' will solve the problem
Another thing needs to be considered is functor
module M (A:sig..end) = struct
  let ...
end
I hope that the signature inferred for functor M hide those functions beginning with '__' as well
(0008178)
doligez (administrator)
2012-09-29 22:39

For the record, I don't like this idea, it looks too much like an ad-hoc hack.
(0008213)
hongboz (developer)
2012-10-07 02:44

Why do you think it's an hack? this is just similar to add an underscore _ to remove the warnings, not theoretical interest, but very beneficial to the ocaml programmers.
To be more disciplined, perhaps we could add something similar to F#, private access modifier?

let private f = blabla?
(0008217)
meyer (developer)
2012-10-07 14:20

I don't like the idea either. Useful thing to do is to have an update mode for mli file. The compiler will just try to update (equiped with command line option) changed entries in mli file.
(0008218)
meyer (developer)
2012-10-07 14:33

I raised 0005777
(0008219)
hongboz (developer)
2012-10-07 16:03

in practice, you never want to read .mli file, the output is too large for generated code. can you explain a bit why you don't like the idea?
(0008220)
meyer (developer)
2012-10-07 20:59

mli files are used to expose the selected interface from the ml files and not other way round. If we start marking functions or types inside ml file to make the generated mli files the result will be the same not generating mli files in the first place. More over people will start abusing underscores in the code, and the underscored functions in my taste don't look very appealing.

Reading mli file is fine, it's not needed to hbe type checked anymore, more over the ml files are usually bigger than mli files, especially in case when we use module types. In the case of the updating the mli file when is needed actually the file will be smaller because the modules that's been not changed will have module type.
(0008222)
gasche (developer)
2012-10-08 11:00

A natural solution to your use-case would be to have the extension also generate the .mli file. However, I'm not sure that can be done conveniently (in my experience camlp4 is good at filtering/transforming a given file, but not that convenient for spitting out other files on the side; still the Ocsigen people do that iirc.), and it could be challenging to do compositionally.

Maybe you could, in the .ml file, generate a module along with its interface, and then include it?

Instead of generating:

    let __foo = bar
    let foo = foobar __foo

you would generate

    include (struct
        let __foo = bar
        let foo = foobar __foo
      end : sig
        val foo : foo_t
      end)
(0008224)
hongboz (developer)
2012-10-08 15:08

yes, I know this can be 80% done in p4, but given the fact that just tens of lines patches in the compiler can solve the problem once and for all, it's not worth the complexity to use p4. Any feature can be abused, i.e, Obj.magic, personally, I can not buy meyer's argument.
(0008231)
lavi (reporter)
2012-10-10 09:51

If I sometimes would like such kind of hiding expressed in ml files, I do not think that the name of a value should impact its visibility.

So a cleaner solution would be e.g. to reuse private keyword:

private let x = ...

and the same for type, class, module...
(0008239)
hongboz (developer)
2012-10-10 15:00

yes, as I said
let private or private let both sound reasonable
(0008367)
turpin (reporter)
2012-10-30 13:12

I prefer "private let", because then you may also add "private type", "private module"...

Also, if this was added to the language, allowing this in any structure (not just toplevel modules) would be less ad hoc.
(0008373)
berenger (reporter)
2012-10-31 01:47

I think, by default everything is private.
You want to be explicit only about what you
want to make visible outside of your module
(what's in the .mli file).

Hence the export or public keyword.

I don't like so much __ because it's some code.
You can't read it loud so it's not as explicit
as using a keyword.
Also, __ as some C taste.
public or private as some C++ taste.
Only export (as in Haskell I think), has a nice taste. ;)
(0008374)
hongboz (developer)
2012-10-31 02:40

berenger, that will break all existing code, definitely not we want.
I am for such a syntax:

private let
private type
private module

module = struct
   private let a = 3
end
(0008376)
berenger (reporter)
2012-10-31 02:55

I did not know about this private keyword, I'll study its use.
I also did not give any thought at backward compatibility, that's true.
(0008380)
frisch (developer)
2012-10-31 17:25

Hongbo: what would be the type of the following structure?

struct
  private type t = A | B
  type s = t * t
end
(0008381)
turpin (reporter)
2012-10-31 17:39

Error: type construcor t escapes its scope

This problem seems similar to the following:

let _ =
  let module M = struct type t = A end in
  M.A
(0008384)
hongboz (developer)
2012-10-31 18:48

For private let and private module, remove its corresponding signature
For private type, it's locally transparent, but its signature is abstract datatype.
Any signature involves a private module will result in a compilation error.

IMO, the output should be
type t
type s = t * t

This will not change the typing rules, just add a filter after type checking.
so you can still play with
private type t = private blabla..
(0008386)
frisch (developer)
2012-10-31 19:05

If you remove submodules, you can break well-formedness of type. How do you deal with:

 private module X = struct type t = A | B end
 type s = X.t

?
(0008387)
turpin (reporter)
2012-10-31 19:12

The same "escapes its scope" sort of errors seems reasonable. I also have encountered other messages like "... cannot be eliminated from the signature", but I don't remember in which cases. I don't see any strong theoretical issue, and I believe the compiler already addresses similar problems for existing constructs.
(0008389)
hongboz (developer)
2012-10-31 19:16

As I said, any signature involves a private module type will result in a compilation error.

If a module M is private, then any type declaration involves M will result in an error.


private module X = struct type t = A | B end
type s = X.t
will result in a compilation error
(0010299)
frisch (developer)
2013-09-03 11:14

While modifying the Bisect instrumenter, we had introduced a bug which would have easily been avoided with the required feature. The modified instrumenter added a global value declaration at the top of the compilation unit, to be used at many places in the unit. The resulting code looks like:

=============================================
  let __bisect_table = Bisect.get_table "/foo/bar/my_module.mf"

  ....
  ....

       <a lot of references to __bisect_table>
=============================================


The __bisect_table identifier ends up in signature inferred for units without an explicit .mli file. The bug then appears for a unit which starts with an "open My_module", where My_module doesn't have an explicit interface. The instrumenter would insert a local "let __bisect_table = ..." above this open statement, and this declaration would then be hidden by the open. All local uses of the __bisect_table identifier would refer to the one exported unintentionally from My_module.

The fix is to use a globally unique name for the identifier (e.g. some hash of the current unit).


---------------------------------------------------------------------------------------------------------


Now, if people agree that the feature would be convenient to have in some form:

1. Instead of using a naming convention (which looks ad hoc) or introducing a new language construct (which might be overkill, considering the request mostly makes sense for generated code), one could use an attribute (as introduced by "extension_points"). This would break the current property that the compiler does not give any meaning to these attributes, though. Opinions?

2. On the implementation side, while it's very straightforward to deal with value declarations (just filter the "bound_idents" list in the Pstr_value case of Typemod.type_structure), it's less clear how to proceed with e.g. type and module declarations (one needs to apply a well-formedness check on the resulting signature, and I don't know if it already exists in the current code base).
(0010300)
frisch (developer)
2013-09-03 11:20

(I've changed the "Summary" to make it less specific.)

- Issue History
Date Modified Username Field Change
2012-09-22 19:22 hongboz New Issue
2012-09-23 04:01 hcarty Note Added: 0008145
2012-09-23 04:13 hongboz Note Added: 0008146
2012-09-29 22:39 doligez Note Added: 0008178
2012-09-29 22:39 doligez Status new => acknowledged
2012-10-07 02:44 hongboz Note Added: 0008213
2012-10-07 14:20 meyer Note Added: 0008217
2012-10-07 14:32 meyer Assigned To => meyer
2012-10-07 14:32 meyer Status acknowledged => assigned
2012-10-07 14:32 meyer Assigned To meyer =>
2012-10-07 14:33 meyer Note Added: 0008218
2012-10-07 16:03 hongboz Note Added: 0008219
2012-10-07 20:59 meyer Note Added: 0008220
2012-10-08 11:00 gasche Note Added: 0008222
2012-10-08 15:08 hongboz Note Added: 0008224
2012-10-10 09:51 lavi Note Added: 0008231
2012-10-10 15:00 hongboz Note Added: 0008239
2012-10-30 13:12 turpin Note Added: 0008367
2012-10-31 01:47 berenger Note Added: 0008373
2012-10-31 02:40 hongboz Note Added: 0008374
2012-10-31 02:55 berenger Note Added: 0008376
2012-10-31 17:25 frisch Note Added: 0008380
2012-10-31 17:39 turpin Note Added: 0008381
2012-10-31 18:48 hongboz Note Added: 0008384
2012-10-31 19:05 frisch Note Added: 0008386
2012-10-31 19:12 turpin Note Added: 0008387
2012-10-31 19:16 hongboz Note Added: 0008389
2013-09-03 11:14 frisch Note Added: 0010299
2013-09-03 11:20 frisch Note Added: 0010300
2013-09-03 11:20 frisch Summary automatically hiding functions beginning with "__" => A way to avoid automatic export of declaration in inferred signatures
2013-09-03 11:20 frisch Assigned To => frisch
2013-09-03 11:20 frisch Status assigned => acknowledged
2013-09-03 13:44 frisch Assigned To frisch =>


Copyright © 2000 - 2011 MantisBT Group
Powered by Mantis Bugtracker