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
Add module _ = X syntax #6662
Comments
Comment author: @gasche Also on the wishlist: module X = M which would be useful as a code preprocessing target (when you want to give them to two module expressions given by the user, without having the name of one risk shadowing free module variables in the other). |
Comment author: @alainfrisch
This can be achieved with: include (... : sig end) |
Comment author: @mshinwell I'm not sure "module _ = X" makes sense in the context of -no-alias-deps, where the initializer for X would not be run. (-no-alias-deps is likely to become a prevalent mode of compilation, I suspect, since it gives arguably a more reasonable semantics for module aliases.) |
Comment author: @lpw25
This is true for regular paths, but they can't include side-effects anyway. If the path includes a functor application then it will always be executed. |
Comment author: @lpw25 Since it is similar, I'll bring up a suggestion that I've made before: supporting This would mean supporting:
and
which is useful for functors that are intended to be used entirely for their side-effects. The idea (similar to generative functors) is that |
Comment author: @gasche I thought of the same today when seeing this request, but:
If, under -no-alias-deps, we don't have a way to force linking of compiler units (which are never functions) using the module language, we will have to use a way to do that in the term language: have the must-be-linked module expose an dummy If the "good style" is to do this for compilation unit whose linking we may want to force, it would seem natural to also follow it in functor bodies: export a (init : unit -> unit) function and do let () = let module M = F(X) in M.init() In short: if the proposed feature works only for non-alias paths, it's maybe not worth it. |
Comment author: @whitequark lpw25, without being able to subtype any module to (), this feature would be useless for my use case (cstubs). |
Comment author: @lpw25
As with generative functors, the point is to ensure that usage matches definition. So you should only be able to write: module () = Foo(X) if module () = Ignore(Foo(X)) where
module () = Ignore(Uid) since it will coerce the module to
Using |
Comment author: @gasche Excellent! |
Comment author: @lpw25
Seems reasonable to me. |
Comment author: @yallop I'm in favour of this proposal. It's an example of the general principle that you shouldn't be obliged to name something when you're not going to actually use the name. |
Comment author: @alainfrisch
It feels strange to force using a binding construct to explicitly avoid giving a name to something. Moreover, this is already possible without using the binding construct: include (... : sig end) or: include Ignore(...) |
Comment author: @yallop I don't really see it as forcing the use of a binding construct: nothing's stopping you from using the 'include' forms, after all. If you're modifying code so that it no longer uses the module name (or starts using it) then it seems quite natural to move between 'module M = F(X)' and 'module _ = F(X)'. |
Comment author: @gasche (It's also strange to use the |
Comment author: @alainfrisch The case of a functor only used for its side-effect is quite rare (and thanks to first-class module, it is also possible to use a regular function in that case), and there is already a way to support that (actually, such a functor would likely return an empty signature already, thus supporting "include F(X)" directly). The proposed feature is only a convenience and I'm not sure that in this specific case, it's worth extending the language to provide this convenience, considering how light the existing alternative is. At least, if one decides to support it, it could be done purely as syntactic sugar (mapping "module _ = ..." to "include (... : sig end)") to avoid changing the Parsetree.
How often does this happen? I can imagine frameworks that make heavy use of functors only for their side-effects, but then you know from the start that the result will always be ignored (and the resulting signature is empty). Are there indeed cases of functors with non-empty output signatures that can be used for their side-effects only in some cases, but not always? |
Comment author: @whitequark Alain, indeed there is at least one: cstubs. It has a signature which include several callable functions in "run" mode, and it invokes side effects that generate stub C code in "binding generation" mode. |
Comment author: @lpw25 Note that the |
Comment author: @garrigue What's wrong with writing |
Comment author: @alainfrisch If you want to avoid introducing a dummy name: ignore (module ... : EMPTY); or: let _ = (module ... : EMPTY) in (with "module type EMPTY = sig end" defined somewhere) |
Comment author: @yallop Here's another argument in favour of this proposal. Since 4.02 you can write '_' in functor bindings functor (_: S) -> ... so it's natural to have the same binding syntax in other module binding contexts. It's good that there are workarounds, but I don't see any drawbacks to simply making the language a little more uniform. Alain, is it the need to change Parsetree that you're concerned about (rather than the change to the language)? |
Comment author: @alainfrisch
Yes, indeed (since I consider the feature to be quite rarely useful). I can think of several ways to support the feature:
In addition to these technical considerations, I also have a general dislike for "_ bindings": I find "let _ = ..." very ugly. Either the thing to be ignored has type unit, and it's better to write "let () = ..." or simply use sequencing; or it doesn't, and I prefer to be explicit about it and use the ignore function. |
Comment author: @lpw25
This would be my personal preference. Using |
Comment author: @gasche On the other hand, Alain's taste would seem to indicate that (module () = ..) is equally or more important than (module _ = ..), and just using an option would not cover that case. |
Comment author: @lpw25
Good point. I would very much like to add the |
Comment author: @alainfrisch If we start finding other useful forms of "module binding patterns" in addition to plain identifiers, I'd indeed be more inclined to "break" the Parsetree (which requires to change many client code -- we don't provide any kind of backward compatibility guarantee, but it's better when breakages are justified by something with a clear benefit). With "_" and "()", the case is already stronger, and I'd rather avoid turning "string" into "string option" now and breaking it again later if/when "()" is introduced. |
Comment author: @gasche It is my understanding that 4.03 will break other things syntax-wise in any case; or did we manage to avoid that yet? (I'm personally interested in pushing the syntax changes #6662, #6800 and #6806, but I may wait until I get a clearer picture of the menhir-for-ocaml work.) I would be in favor of having at least (module () = ...) in the next release that does open the pandora box of AST changes, whichever it is. (Also, because I don't think we can stabilize the AST format, it might be good to get some actual experience of AST breakage for ppx users to understand the typical pain points and required changes, before trying to design/provide compatibility libraries to alleviate this issue.) |
Comment author: @gasche (Of course I don't mean that breaking the parsetree once means that "anything goes" until the next release, but rather than we have to think in term of a batch of small changes rather than each change as a problematic breakage in isolation.) |
Comment author: @garrigue But we still fallback on the problem that none of these are really useful. |
Comment author: @lpw25
Adding |
Comment author: @garrigue Is it not enough for the module to have an empty signature? I find this discussion a bit strange, because this seems to assume that using functors only for their side effects is a common practice. I know that some people do that, but do we really want to promote that? Note that we do not prevent it in any way, since all this is already doable, without name space leaks. |
Comment author: @lpw25
It is different in the same way that:
is different from
The idea is as follows:
The point is to ensure that the intention of the functor's author and the intention of the functor's user are the same. If we have:
and
this clearly indicated that both the author and user of
because This is similar to the benefits of writing:
instead of
|
Comment author: @yallop This feature would also offer a convenient way of checking that a module satisfies a signature without introducing a binding into the environment. For example, when defining a module module Num = it's sometimes useful to be able to check at the point of definition that the module satisfies one or more interfaces: module _ : ORDERED = Num |
Comment author: @gasche I'm not sure why we are arguing about this. Both (module _ = ...) and (module () = ...) are natural, consistent syntax with several proposed used-cases, and it seems fairly easy to implement. (There is a cost to changing the parsetree definition, but hopefully by now a lot of the parsetree consumers have found a compatibility-story they liked?) |
Comment author: @alainfrisch
I don't think so. |
I believe this has been fixed by #8908 |
Original bug ID: 6662
Reporter: @whitequark
Status: acknowledged (set by @damiendoligez on 2014-12-24T15:46:14Z)
Resolution: open
Priority: normal
Severity: feature
Category: language features
Related to: #6362 #6821
Monitored by: @gasche @diml @ygrek @hcarty @Chris00
Bug description
This would be consistent with other bindings and useful e.g. for cstubs, where you often need to execute a functor only for side effects.
The text was updated successfully, but these errors were encountered: