Skip to content
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

Allow to define explicit "impure" (i.e. generative) functors #5905

Closed
vicuna opened this issue Jan 24, 2013 · 13 comments
Closed

Allow to define explicit "impure" (i.e. generative) functors #5905

vicuna opened this issue Jan 24, 2013 · 13 comments
Assignees
Milestone

Comments

@vicuna
Copy link

vicuna commented Jan 24, 2013

Original bug ID: 5905
Reporter: @garrigue
Assigned to: @garrigue
Status: closed (set by @xavierleroy on 2016-12-07T10:47:25Z)
Resolution: fixed
Priority: normal
Severity: feature
Version: 4.01.0+dev
Target version: 4.01.1+dev
Fixed in version: 4.02.0+dev
Category: typing
Tags: patch
Related to: #6821
Monitored by: bobot @hcarty @mmottl

Bug description

For some applications, not having the possibility to declare a functor as generative is a problem.
For instance when you define a global map through a functor

module DB : functor (X : struct end) -> sig
type key
let new_key : unit -> key
let write : key -> string -> unit
let read : key -> string
end

module MyDB = DB(struct end)

The above works fine (i.e. keys are restricted to a single DB) as long as nobody writes the following code:

module DB1 = DB(String)
module DB2 = DB(String)

which makes DB1.key and DB2.key equal.

The problem is that to my best knowledge there is no workaround.

My proposal is a rather stupid one: add some syntax for impure functors,
that are not using their argument, but must be generative.

module DB () = ...
module DB = functor () -> ...
module M = DB ()
module DB : functor () -> ...

Simplest implementation: all this is just syntactic sugar for an argument of name "*" and of type "sig end", and forcing the functor application to be generative in that case.
Patch included.

@vicuna
Copy link
Author

vicuna commented Jan 24, 2013

Comment author: @alainfrisch

I agree it would be very nice to have generative functors in addition to transparent ones. But why should this notion require a "dummy" argument? It is quite common to have functors taking a non-trivial argument, which should be generative (typical example: a hash-consing functor). I'd rather have a different kind of functors (unfortunately, this requires more innovation on the syntactic side than your proposal).

Also, generative functors enable something very useful: unpacking of first-class modules within/as the functor body. This can be used, for instance, to choose at runtime between two implementations of the functor, based on some condition. With runtime types (or a manual version based on GADTs), it is even possible to customize the functor's behavior according to the structure of the abstract types in the argument (example: implement sets as Patricia trees if the elements are integers).

Would your proposal make this possible for generator functors?

@vicuna
Copy link
Author

vicuna commented Jan 25, 2013

Comment author: @garrigue

The idea of using a "dummy" argument is that it requires no new syntax (or very little).
And it is just as expressive: you just have to add () as last argument to make your functor generative.

The discussion should rather be: since generativeness is a property of functors, is it useful to mark it in functor applications. I believe that this is the case, as this is the application, not the functor, that behaves differently.

I have also updated the patch so that it is now possible to unpack first class modules inside impure functors:

module type S = sig val x : int end;;
let v = (module struct let x = 3 end : S);;
module F() = (val v);;

@vicuna
Copy link
Author

vicuna commented Jan 25, 2013

Comment author: @garrigue

Fixed the patch: if we are to allow unpacking inside impure functors, then we should not allow applying them inside an applicative body...

@vicuna
Copy link
Author

vicuna commented Jan 31, 2013

Comment author: @garrigue

Fixed the patch again: one cannot coerce an impure functor into an applicative one.
The patch is in trunk/experimental/garrigue/impure-functors.diff

@vicuna
Copy link
Author

vicuna commented May 21, 2013

Comment author: @alainfrisch

FWIW, I believe that extending the language with generative functors would indeed be very useful, especially if it allows to unpack first-class modules in their body.

@vicuna
Copy link
Author

vicuna commented Jun 18, 2013

Comment author: @alainfrisch

Now that 4.01 is branched, what about including the patch in the trunk? It seems nobody objects to it, and there are at least two supporters.

@vicuna
Copy link
Author

vicuna commented Sep 20, 2013

Comment author: @alainfrisch

Jacques: do you think your patch is ready for inclusion, or are you aware of some problems with it?

@vicuna
Copy link
Author

vicuna commented Sep 20, 2013

Comment author: @garrigue

I don't recall any problem, but of course I would check everything again before inclusion.
Some parts may have to be discussed, since I tried to be the less intrusive as possible.

@vicuna
Copy link
Author

vicuna commented Oct 10, 2013

Comment author: @lpw25

An example that should be prevented:

module F () : sig type t end = struct module M = (val ...) type t = M.t end

module G (X : struct end) : sig type t end = struct
module L = F ()
type t = L.t
end

module N1 = G(X)
module N2 = G(X)

where ... is some first-class module expression.

This would have N1.t equal to N2.t even though they may be different types (because they come from unpacking a first-class module).

@vicuna
Copy link
Author

vicuna commented Nov 13, 2013

Comment author: @garrigue

This is already taken care of:

module type S = sig val x : int end
let v = (module struct let x = 3 end : S)
module F() = (val v);;

module G (X : sig end) : S = F ();;

Error: This kind of expression is only allowed inside impure functors.

@vicuna
Copy link
Author

vicuna commented Nov 15, 2013

Comment author: @lpw25

This idea is only half-baked, but I was wondering if the same mechanism could be used to express functors that are only used for their side-effects. Something like:

module F (X : sig ... end) : () = struct
...
do some effectful thing
...
end

module () = F(Foo)

so the functor is given () as its return type, and () can only be given to the module expression (). So the following would be an error:

module M = F(Foo)

similarly, ignoring the result of a normal functor would be an error:

module () = Map.Make(List)

Note that since these side-effect only functors can not create any types, I think they can safely be used within an applicative functor even if they are "impure".

@vicuna
Copy link
Author

vicuna commented Nov 18, 2013

Comment author: @garrigue

I'm not sure we need new syntax for the return type part.
But you're right on one point: if we now that a generative functor returns no types, then it should be safe to call it inside an applicative one.
Same thing applies to unpacking.
Both can be useful in practice.

@vicuna
Copy link
Author

vicuna commented Dec 17, 2013

Comment author: @garrigue

Patch merged to trunk at revision 14365.
It allows opening first class modules and applying generative functors inside applicative functors, if no types are created.

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

No branches or pull requests

2 participants