Support inherited field in object type expression#1118
Conversation
Also add location on object field label and polymorphic variant tags.
|
class type t1 = object method f : int end
type t = < #t1 >
Update (04/06): class type t1 = object method f : int end
type t = < t1 >is supported now |
|
this looks really useful, structural typing are very useful for projects like JSOO and BuckleScript. some bikeshedding about syntax, type t = < x : int ; y : int; inherit g > |
There's no punning anywhere in the type algebra so I doubt it will confuse people. Whereas using |
class type c = object method x: int end
class type d = object inherit c method y: int end |
|
So maybe it's more accurate to say that |
I know and it's kind of wrong there too. The class system has quite a few things like this which subtly confuse different concepts, and I've always thought that it was one of the reasons people have a lot of trouble understanding it. I'd rather not add one more to the list, even if it would not be the worst offender. |
|
I like the syntax that @objmagic has implemented because it parallels the syntax for polymorphic variants: type d = [`A of int | `B of int | c ]
type d = < a : int ; b : int ; c >Rather less importantly, it's also reminiscent of the functional update syntax for records: { r with x = a ; y = b }
< r ; x : a ; y : b > |
This is the killer argument, and I think it concludes the syntactic discussion. Any takers for an actual code review? :-) |
There was a problem hiding this comment.
It'd be good to have a few more tests, e.g. where an extended type is more than just a simple name
type 'a t = < m: 'a >
type s = < int t >
module M = struct type t = < m: int > end
type u = < M.t >
type r = < a : int; < b : int > >and where an extended type is empty
type e = < >
type r1 = < a : int; e >
type r2 = < a : int; < > >|
@yallop would you like to also have a look at my question at #1118 (comment)? @Octachron would you like to help review change on odoc? I was thinking to add some tests for that but I do not know how to do that. |
|
I've been thinking of adding this features a number of times. For the implementation, I'm a bit concerned by the way |
|
@garrigue I agree that implementation should be symmetrical with polymorphic variants' implementation. However I don't understand your statement "the restriction on duplicate field declarations doesn't seem relevant for object types". Do you mean |
|
@objmagic No, this should give you a unification error, like for polymorphic variants. We're talking about objects, not modules. |
|
@objmagic On the ocamldoc side, if you want to add tests, there are two kind of tests available in the testsuite. First, basic type printing tests are located in
Adding an Looking at the code and ocamldoc output, currently the information about inherited object type is not used by ocamldoc. Consequently type t = <a:int (** comment for a *) ; b:int (** comment for b*) >
type r = < t (**comment for inherited t*) ; c:int (** comment for c*) >is printed as type t = <a:int (** comment for a *) ; b:int (** comment for b*) >
type r = < a:int; b:int ; c:int (** comment for c*) >Ideally, I think it would be better to not expand inherited fields in the documentation and add the possibility to comment on inherited fields. However, these are quality of life improvements, and could be the object of a subsequent PR, since ocamldoc is neither crashing nor missing object fields in presence of inherited fields. |
ocamldoc/odoc_sig.ml
Outdated
| | Otag ({txt=name}, _, ct) :: | ||
| ((Oinherit ct2) as ele2) :: q | ||
| | Otag ({txt=name}, _, ct) :: | ||
| ((Otag (_, _, ct2)) as ele2) :: q -> |
There was a problem hiding this comment.
It might be worthwhile to fusion the two cases here into
| (Otag ({txt=name}, _, ct) ) :: ((Oinherit ct2 | Otag (_, _, ct2) ) as ele2) :: q| let pos2 = Loc.ptyp_start ct2 in | ||
| let (_,comment_opt) = just_after_special pos pos2 in | ||
| (name, comment_opt) :: (f (ele2 :: q)) | ||
| | _ :: q -> f q |
There was a problem hiding this comment.
Note that the OInherit _ case is essentially missing here.
There was a problem hiding this comment.
@Octachron Thanks for your review. Coming back to this comment after three months, I am still not sure what to do here because inherited field gets expanded in documentation (in elsewhere as well, e.g. toplevel display). This means when we encounter an inherited field (Oinherit of core_type), we want to fetch documentation associated with it somewhere, which means we need to save doc generation result for all object type declaration. I am not sure how to handle cross-module/file case either. Any suggestion is welcomed here...
There was a problem hiding this comment.
@objmagic, sorry for the delayed answer. I will try to have by a look by the end of the month to see if it is possible to implement documentation comment for inherited fields in a reasonable way within ocamldoc. However, I don't think that non-optimal support within ocamldoc should block integration ((inlined record had a quite imperfect support for a time, and documentation comment can still not be attached to polymorphic variants)).
That's the correct behaviour, since class type t1 = object method f : int end
type t = < t1 > |
Also add test for polymorphic variants.
|
@garrigue Ping again. What do you or |
|
Should check it in more detail, but the feature is straightforward and the behavior reasonable. Ultimately, since this is a new feature, I think we need a consensus: discuss it on caml-devel or at a physical meeting. |
|
If the code is good (and I'm not competent to review it), I would be in favor of taking the feature in. It does not add complexity (especially given that polymorphic variants already support it), and I don't find the redundancy with class types an issue here -- and I have used object types myself in situations where this would have been handy. |
|
The feature seems generally reasonable to me. What is the desired behavior in case two methods have the same name? Also, considering that repeated method declarations are currently not allowed: it might make sense to allow this if it is allowed indirectly (via inheritance). |
|
@fpottier Thanks for your review.
I agree. And I'd like to hear other people's opinion of your comments as well. |
|
When both inherited types have methods with the same names I think unifying the types of the methods is the best approach. OCaml already performs unification when two inherited polymorphic variable types have the same tag. For example, in the following code # fun (_ : ('a * 'b)) x -> (x : [ [`M of 'a] | [`M of 'b] ]);;
- : 'a * 'a -> [ `M of 'a ] -> [ `M of 'a ] = <fun>With this PR the behaviour for object types is similar, unifying the types of the methods: # fun (_ : ('a * 'b)) x -> (x : < <m: 'a> ; <m: 'b> >);;
- : 'a * 'a -> < m : 'a > -> < m : 'a > = <fun>This PR also relaxes the constraint that currently prohibits having multiple occurrences of methods with the same name. In OCaml 4.05.0: # type 'a t = [`M of 'a | `M of int];;
type 'a t = [ `M of 'a ] constraint 'a = int
# type 'a t = <m: 'a ; m: int>;;
Characters 12-28:
type 'a t = <m: 'a ; m: int>;;
^^^^^^^^^^^^^^^^
Error: This is the second method `m' of this object type.
Multiple occurrences are not allowed.With this PR: # type 'a t = <m: 'a ; m: int>;;
type 'a t = < m : 'a > constraint 'a = int |
|
Yes I just realized this has been implemented. I have updated tests. |
I'd like to hear what @garrigue has to say about this point. |
|
If we seek symmetry with polymorphic variants, I think it is natural to remove this constraint. |
garrigue
left a comment
There was a problem hiding this comment.
The implementation is clean, and in clear symmetry with polymorphic variants.
If nobody objects, I propose merging this before 4.06.
|
I think that merging earlier is better than later to avoid conflicts. I'm planning to go ahead and merge in a few days if nobody does it before or objects. I checked that the CI failures are not an issue (they are failure in Camlp4 currently not building under trunk). |
|
@gasche Sounds good. I agree that if this PR looks all good, merge it sooner so that we don't need to deal with more merge conflicts. |
…objects PR ocaml#1118 had turned polymorphic variant constructors from 'label' to its definition 'string' for consistency with field names in object types; instead, we consistently name 'label' the method and instance variable names throughout the AST. This does not break compatibility as the two types are synonym, but it should improve readability of parsing/parsetree.mli.
|
I made a mistake when assessing the travis CI results: it is indeed this PR that is breaking Camlp4, and this should be fixed. In the meantime, #1265 proposes to temporarily disable camlp4 building to avoid breaking other PRs as well. |
|
See camlp4/camlp4#123. |
fix camlp4 build after ocaml/ocaml#1118
4.06 incompatibility was caused by changes to Typedtree in ocaml/ocaml#1118 and ocaml/ocaml#1249.
4.06 incompatibility was caused by changes to Typedtree in ocaml/ocaml#1118 and ocaml/ocaml#1249.
* Add layout to params of primitives * update tests
This PR allows one to inherit closed object type expression when defining object type.
Here is an example. Entering the following code in interpreter
and you will have the following output
This is an OCaml Labs compiler hacking task. Also, there was a thread on caml-list last month discussing about it.
One small refactor along with this PR is that I added location on object field label and polymorphic variant tags. See parsetree.mli#L156.