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 []
as a user-defined constructor
#234
Allow []
as a user-defined constructor
#234
Conversation
I'm all in favor of that. It's basically a little piece of #76 but It should be enough for interesting uses. I personally think the syntax, for uniformity, should be: module H = struct
type 'a hlist =
[] : unit hlist
| (::) : 'a * 'b hlist -> ('a * 'b) hlist
end |
I'm OK with both of the syntaxes. |
For consistency, definitely demand |
I uncommented another line to allow |
Notice a nice use case for that:
It would allow to safely write that kind of mapping on a set of values. |
I don't see the point of allowing overriding of all the of basic syntax. For every OCaml user, [] is expected to be the empty list, idem for :: and [a;b;c]; breaking this assumption makes the code unreadable, unmaintenable by other developers, incompatible with other libraries, etc. So, the only consequence that I can see of accepting such patch will be a strong recommendation in the user manual to never use that possibility... So, why add features just to recomment later not to use them ??? |
It's not possible to have confusion for the unaware user without getting a warning about disambiguation, at the very least, and it makes some pattern much easier to express. I don't see how it's different from various other recent additions to the language. |
For the same reason OCaml allows overriding of operators. |
@Drup: the warning about disambiguation is going to disappear at some point in the future. At that point, there will be nothing to save the "unaware user". I think we have a choice: either we want to make the language simple and easy, to encourage new users without a PhD in type theory to join us, or we want to make the most complex language in the world, with features that nobody use but so that we can say: oh, it's cool, I can write |
It's not a mistake its a useful feature. |
Because Please. |
@lpw25: indeed, one feature I really enjoy in OCaml is having the possibility to make "every value in every program" "completely unique": the recommended coding style should be to never @Drup: Yes, I am wondering why the beginner does not understand simple error messages:
I like compositionnality... |
I wasn't suggesting your argument is equivalent to saying that there should only be one thing called |
My argument is that things that cannot be uniquely identified should not exist in a program: so, Anyway, as the example with arrays was showing, you reach the limits of overriding (+) so fast that it's really not worth allowing it in the first place. |
That example is completely fabricated, I can't really see how that constitutes "so fast". In practice it is fine to use such overloading and it can lead to much clearer code. |
I agree. module H = struct
type 'a hlist =
[] : unit hlist
| :: : 'a * 'b hlist -> ('a * 'b) hlist
end Say user writes this module. We can assume that user understands what bad thing is going to happen if he opens his module later. It is analogous to the case in which user opens On the other hand, if user wants to open such module which is written by someone else, he should somehow understand the possibility of polluting outer environment. For example, |
I think this feature is nice as it removes a bit of magic around the And it's impossible to prevent people from writing unreadable code, so I don't think we should use this as an argument against this specific feature. |
@lpw25: "In practice, it is fine..." I have written alone projects of nearly 100klines of OCaml code, so I think I have quite an experience in the kind of code that can be written in such projects. It is not "fabricated", it is exactly what appears when you move from short examples to real life code. |
Are you saying that the example was taken from a real piece of code, or are you objecting to my use of the word "fabricated"? Perhaps it is too strong. My point is drawing a very general conclusion "using operator overloading is always a mistake" from a small sample of code is not particularly persuasive. |
I don't think your example is particularly illustrative on this point. It could be easily rewritten as let sum t x = Int32.(t.(x) + t.(succ x) )
(* or *)
let sum t x = Int32.( t.(x) + t.( Int.add 1 x ) ) At the same time, for numerical and mathematics oriented applications, the I think the same thing is true for the list literal syntax; there is quite a few |
Anyway, whatever, I gave my opinion. PR are nice to improve the distribution tools and libraries. I think they are a bad way to improve the language itself, by small inconsistent increments and no long term vision. |
I, for one, am supportive of this PR. Less magic is a good thing. I also acknowledge the danger inherent in local opens. I think the real problem is the inability to restrict local opens easily. For example,
Would be much safer than using Int32 directly. Currently, to do this, we'd have to write a whole module type, which involves the type of every function we want (as is shown above), and is tedious. A quicker selective syntax as suggested in the first alternative would really help, but I don't think this particular syntax works because it clashes with functor application. Anyone have any ideas? |
module Int32 = struct
let (+) = Int32.add
...
end |
@ibnfirnas: Good point, you could either restrict the type view of a module or build another module altogether -- I didn't think of that. I agree that building a new module is cleaner syntactically, but it's still not concise enough that people would use it in every file. I guess stating the point of this is important: you want to make it such that the included functions/types/etc can be concisely specified in every file. This prevents the problem you open yourself up to by opening a file either locally or globally, whereby a change in one file causes a possibly invisible semantic change in another file. In other words, suppose module Foo has no (+), and I locally open it in another file, bar.ml. Many years later, I add (+) to Foo, not realizing that it's opened locally in bar.ml. Calls to (+) in Bar now go to Foo's version of (+) rather than the default library's version. Hilarity and painful debugging ensues. If Foo was first constrained to only the used functions, any addition to Foo would not have been reflected in bar.ml. However, to be feasible as a common practice, this must be as painless a process as possible, which means it should really be a one line thing with minimal syntactic footprint IMO. |
@damiendoligez Right now, |
@chambart I guess the meaning of this map is to guarantee returned list has the same length? It is cool but I would like to know in which case it will be useful? |
@lpw25: Yesterday, I was not completely satisfied by my reply, but I couldn't put the finger on the problem. And then, I realized why: in fact, my example is completely real, it is indeed code that I have written in the past, and now, I remember where! In try.ocamlpro.com, from the beginning, we added an extension to locally rewrite constants. For example, you can type:
As you can see, integers and operators are overriden in the In the OCamlPro compiler (the one used by Memprof), we have tested such features for a long time, and our conclusion was often: once you have made the patch, you usually play with it on small examples for a few days; but then, in bigger projects, the new feature is never used. Finally, we usually decide to drop them after one or two years. In conclusion, I would argue that people here should refrain from saying "it's worderful, I want it !", instead, people should take such PRs, merge them in their own local version of OCaml, and use them for one year in their projects. Then, after one year, they should tell us whether it was the wonderful feature that solved all their problems, or just a nice idea, but with little interest. |
@marklrh
|
I agree with @lefessan , local open (overloading) makes code hard to reason about M.(f a b) (* all functions inside M can only be imported from M*) Edit: typo |
@marklrh
That's not exactly right: as evidenced by Mantis #5936, most people expect |
@gasche any opinion on this issue during developer meeting? |
No, we didn't have time to cover all pull requests and open bugs, and this one was not discussed, sorry. |
I think this boils down to the mental representation of At any rate, it's really strange to have |
Generally speaking, overloading built-in operators/syntax/constructors/... seems a rather bad idea in terms of readability, but I don't really object to the PR. FWIW, I consider |
ping again. any consensus on getting this merged? more discussion? |
@damiendoligez @gasche any plan on merging this in 4.03.0? Is there any concern? I would love to fix any problem. |
@@ -23,6 +23,9 @@ Language features: | |||
- GPR#88: allow field punning in object copying expressions: | |||
{< x; y; >} is sugar for {< x = x; y = y; >} | |||
(Jeremy Yallop) | |||
* GPR#234: allow "[]" as a user-defined type constructor. Demand parenthesis | |||
around "::" when using "::" as type constructor. |
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.
"type constructor" is the wrong phrase here. You should just say "user-defined constructor" (in both instances).
36eff90
to
4f2ad78
Compare
@damiendoligez Rebased against trunk, and made Change entry better |
I am going to merge this, since @damiendoligez has approved it, and there have been no further objections for more than one month. |
GPR#234: Allow ``[]`` as a user-defined constructor
This patch allows empty list as a user-defined constructor name. Together with pull request #144 , one can write the following heterogeneous list.