* [Caml-list] How to rename a record field
@ 2018-09-06 11:36 Enrico Tassi
2018-09-06 11:48 ` Gabriel Scherer
2018-09-06 16:20 ` Alain Frisch
0 siblings, 2 replies; 12+ messages in thread
From: Enrico Tassi @ 2018-09-06 11:36 UTC (permalink / raw)
To: caml-list
Dear caml-list, while refining an API I felt the need to rename a record
field. I'd like to be nice to my users and have the API evolve without
breaking compilation but eventually emitting deprecation warnings.
What I'd like to do, concretely is to move from this
record t = { bad : int; stuff : ty }
to
record old = { bad : int [@deprecated "use good"]; stuff : ty }
record t = old = { good : int; stuff : ty }
The last line does not type check since old and good are different
names. What I'd like to obtain is to have bad and good act as synonyms,
and have OCaml generate a warning when bad is used.
This is pretty much what you would do for a function: leave the old one
in place and stick a deprecate attribute to it.
Is there a way to achieve that for records?
Best,
--
Enrico Tassi
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-06 11:36 [Caml-list] How to rename a record field Enrico Tassi
@ 2018-09-06 11:48 ` Gabriel Scherer
2018-09-06 13:03 ` Enrico Tassi
2018-09-06 16:20 ` Alain Frisch
1 sibling, 1 reply; 12+ messages in thread
From: Gabriel Scherer @ 2018-09-06 11:48 UTC (permalink / raw)
To: Enrico Tassi; +Cc: caml users
[-- Attachment #1: Type: text/plain, Size: 1519 bytes --]
A couple years ago I had a similar feature request for variant (sum)
constructors, and Jacques Garrigue explained that it is in fact quite
difficult. (The reasons apply to record fields as well.) I suspect that you
are out of luck.
MPR#7102: Ability to re-export a variant definition with renamed
constructors?
https://caml.inria.fr/mantis/view.php?id=7102
On Thu, Sep 6, 2018 at 1:36 PM Enrico Tassi <enrico.tassi@inria.fr> wrote:
> Dear caml-list, while refining an API I felt the need to rename a record
> field. I'd like to be nice to my users and have the API evolve without
> breaking compilation but eventually emitting deprecation warnings.
>
> What I'd like to do, concretely is to move from this
>
> record t = { bad : int; stuff : ty }
>
> to
>
> record old = { bad : int [@deprecated "use good"]; stuff : ty }
> record t = old = { good : int; stuff : ty }
>
> The last line does not type check since old and good are different
> names. What I'd like to obtain is to have bad and good act as synonyms,
> and have OCaml generate a warning when bad is used.
>
> This is pretty much what you would do for a function: leave the old one
> in place and stick a deprecate attribute to it.
>
> Is there a way to achieve that for records?
>
> Best,
> --
> Enrico Tassi
>
> --
> Caml-list mailing list. Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>
[-- Attachment #2: Type: text/html, Size: 2301 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-06 11:48 ` Gabriel Scherer
@ 2018-09-06 13:03 ` Enrico Tassi
2018-09-06 13:17 ` Jacques Garrigue
0 siblings, 1 reply; 12+ messages in thread
From: Enrico Tassi @ 2018-09-06 13:03 UTC (permalink / raw)
To: caml-list
On Thu, Sep 06, 2018 at 01:48:23PM +0200, Gabriel Scherer wrote:
> MPR#7102: Ability to re-export a variant definition with renamed
> constructors?
> https://caml.inria.fr/mantis/view.php?id=7102
Thanks for the pointer, but I'm not sure I understand how the arguments
made there apply to records.
My intuition is that, given that records are compared by name, there
should be no difference between adding the equation t = old or
casting (with Obj.magic) each and every term of type old to type t.
To my knowledge the two records have the very same representation at run
time, that does not depend on the names of the fields but just their
types.
I've even tried to disable the check in typing/includecore.ml around
line 230 and the following silly test seems to work.
type old = { bad : int; stuff : bool }
type t = old = { good : int; stuff : bool }
let rb = { bad = 3; stuff = false }
let rg = { good = 3; stuff = false }
let _ = Printf.printf "%b\n" rb.stuff
let _ = Printf.printf "%b\n" rg.stuff
let _ = Printf.printf "%d\n" rb.bad
let _ = Printf.printf "%d\n" rg.good
let _ = Printf.printf "%d\n" rg.bad
let _ = Printf.printf "%d\n" rb.good
let _ = Printf.printf "%d\n" { rb with good = rb.bad + 1 }.bad
let _ = Printf.printf "%b\n" { rb with stuff = not rg.stuff }.stuff
Where is my intuition wrong?
Why records needs to be compared by name and not by (type) structure?
Sorry if the question is silly, but I really don't know.
Best,
--
Enrico Tassi
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-06 13:03 ` Enrico Tassi
@ 2018-09-06 13:17 ` Jacques Garrigue
2018-09-06 14:18 ` Enrico Tassi
0 siblings, 1 reply; 12+ messages in thread
From: Jacques Garrigue @ 2018-09-06 13:17 UTC (permalink / raw)
To: Enrico Tassi; +Cc: Mailing List OCaml
On 2018/09/06 22:03, Enrico Tassi wrote:
>
> On Thu, Sep 06, 2018 at 01:48:23PM +0200, Gabriel Scherer wrote:
>> MPR#7102: Ability to re-export a variant definition with renamed
>> constructors?
>> https://caml.inria.fr/mantis/view.php?id=7102
>
> Thanks for the pointer, but I'm not sure I understand how the arguments
> made there apply to records.
Records being the dual of variants, all the arguments apply.
Namely, if the name is allowed to change, field access can no longer
be seen as the intuitive projection (by name rather than position, since
only name information is available in the source code).
Field name disambiguation works in the same way too.
There is also the same problem with GADTs assuming that two
record types with different field names are distictinct.
The fact the representation is identical is just an implementation detail.
Tuples too share the same representation.
Jacques Garrigue
> My intuition is that, given that records are compared by name, there
> should be no difference between adding the equation t = old or
> casting (with Obj.magic) each and every term of type old to type t.
> To my knowledge the two records have the very same representation at run
> time, that does not depend on the names of the fields but just their
> types.
>
> I've even tried to disable the check in typing/includecore.ml around
> line 230 and the following silly test seems to work.
>
> type old = { bad : int; stuff : bool }
> type t = old = { good : int; stuff : bool }
>
> let rb = { bad = 3; stuff = false }
> let rg = { good = 3; stuff = false }
>
> let _ = Printf.printf "%b\n" rb.stuff
> let _ = Printf.printf "%b\n" rg.stuff
> let _ = Printf.printf "%d\n" rb.bad
> let _ = Printf.printf "%d\n" rg.good
> let _ = Printf.printf "%d\n" rg.bad
> let _ = Printf.printf "%d\n" rb.good
> let _ = Printf.printf "%d\n" { rb with good = rb.bad + 1 }.bad
> let _ = Printf.printf "%b\n" { rb with stuff = not rg.stuff }.stuff
>
> Where is my intuition wrong?
>
> Why records needs to be compared by name and not by (type) structure?
> Sorry if the question is silly, but I really don't know.
>
> Best,
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-06 13:17 ` Jacques Garrigue
@ 2018-09-06 14:18 ` Enrico Tassi
2018-09-06 23:34 ` Jacques Garrigue
0 siblings, 1 reply; 12+ messages in thread
From: Enrico Tassi @ 2018-09-06 14:18 UTC (permalink / raw)
To: caml-list
On Thu, Sep 06, 2018 at 10:17:25PM +0900, Jacques Garrigue wrote:
> Records being the dual of variants, all the arguments apply.
> Namely, if the name is allowed to change, field access can no longer
> be seen as the intuitive projection (by name rather than position, since
> only name information is available in the source code).
> Field name disambiguation works in the same way too.
Thanks. I see why in the general case this makes the source code
ambiguous.
> > type old = { bad : int; stuff : bool }
> > type t = old = { good : int; stuff : bool }
But in a case as simple as this one where names are either different
or occur in the same position I believe there is no ambiguity.
"stuff" would always resolve to position 2 while both
"good" and "bad" to position 1.
I'm not knowledgeable enough to follow your reasoning about GADTs.
Maybe the approach I propose is really unsound, even if I fail to get
it. Still I believe the use case (renaming + deprecating a record field)
is relevant. Any other approach (especially not requiring to patch OCaml)
would make me happy.
Best,
--
Enrico Tassi
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-06 11:36 [Caml-list] How to rename a record field Enrico Tassi
2018-09-06 11:48 ` Gabriel Scherer
@ 2018-09-06 16:20 ` Alain Frisch
2018-09-06 16:21 ` Alain Frisch
1 sibling, 1 reply; 12+ messages in thread
From: Alain Frisch @ 2018-09-06 16:20 UTC (permalink / raw)
To: Enrico Tassi, caml-list
On 06/09/2018 13:36, Enrico Tassi wrote:
> Dear caml-list, while refining an API I felt the need to rename a record
> field. I'd like to be nice to my users and have the API evolve without
> breaking compilation but eventually emitting deprecation warnings.
If you could go back in time, the best solution might be to avoid
exposing a concrete record type, but rather a constructor function (and
possibly deconstructors, if needed). Thanks to optional arguments in
particular, this enables a smoother experience for evolving the API
(e.g. by adding new fields, with a natural default value).
Btw, it might be interesting to allow deprecating the presence or
absence of fields.
-- Alain
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-06 16:20 ` Alain Frisch
@ 2018-09-06 16:21 ` Alain Frisch
2018-09-06 23:45 ` Jacques Garrigue
0 siblings, 1 reply; 12+ messages in thread
From: Alain Frisch @ 2018-09-06 16:21 UTC (permalink / raw)
To: Enrico Tassi, caml-list
On 06/09/2018 18:20, Alain Frisch wrote:
> Btw, it might be interesting to allow deprecating the presence or
> absence of fields.
Sorry, I meant the presence or absence of *optional arguments*, of course.
-- Alain
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-06 14:18 ` Enrico Tassi
@ 2018-09-06 23:34 ` Jacques Garrigue
0 siblings, 0 replies; 12+ messages in thread
From: Jacques Garrigue @ 2018-09-06 23:34 UTC (permalink / raw)
To: Enrico Tassi; +Cc: Mailing List OCaml
On 2018/09/06 23:18, Enrico Tassi wrote:
>
> On Thu, Sep 06, 2018 at 10:17:25PM +0900, Jacques Garrigue wrote:
>> Records being the dual of variants, all the arguments apply.
>> Namely, if the name is allowed to change, field access can no longer
>> be seen as the intuitive projection (by name rather than position, since
>> only name information is available in the source code).
>> Field name disambiguation works in the same way too.
>
> Thanks. I see why in the general case this makes the source code
> ambiguous.
>
>>> type old = { bad : int; stuff : bool }
>>> type t = old = { good : int; stuff : bool }
>
> But in a case as simple as this one where names are either different
> or occur in the same position I believe there is no ambiguity.
> "stuff" would always resolve to position 2 while both
> "good" and "bad" to position 1.
Suppose that (maybe in another module) you have
type u = old = {bad : int; good : bool }
Now, a piece of code containing
x.good
could mean either (x : old).bad or (x : old).stuff, with x having a type
compatible with both t and u simultaneously.
Here it happens that int and bool being incompatible, a wrong interpretation
would probably cause an error somewhere, but this looks rather unpredictable.
Worse, in ocaml it is not always decidable whether two types might be equal
or not, so a definition time restriction could be hard to predict too.
Jacques Garrigue
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-06 16:21 ` Alain Frisch
@ 2018-09-06 23:45 ` Jacques Garrigue
2018-09-07 8:36 ` Alain Frisch
0 siblings, 1 reply; 12+ messages in thread
From: Jacques Garrigue @ 2018-09-06 23:45 UTC (permalink / raw)
To: Alain Frisch; +Cc: Enrico Tassi, Mailing List OCaml
On 2018/09/07 01:21, Alain Frisch wrote:
>
> On 06/09/2018 18:20, Alain Frisch wrote:
>> Btw, it might be interesting to allow deprecating the presence or absence of fields.
>
> Sorry, I meant the presence or absence of *optional arguments*, of course.
Interesting idea.
This should be easily doable, for instance by extending the Asttypes.arg_label type.
By deprecating the absence, do you mean having a warning when the argument is ommited?
Wouldn’t it mean having two distinct deprecation annotations for optional arguments?
Jacques
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-06 23:45 ` Jacques Garrigue
@ 2018-09-07 8:36 ` Alain Frisch
2018-09-07 10:13 ` Jacques Garrigue
0 siblings, 1 reply; 12+ messages in thread
From: Alain Frisch @ 2018-09-07 8:36 UTC (permalink / raw)
To: Jacques Garrigue; +Cc: Enrico Tassi, Mailing List OCaml
On 07/09/2018 01:45, Jacques Garrigue wrote:
> Interesting idea.
> This should be easily doable, for instance by extending the Asttypes.arg_label type.
One could also just keep the information on the value binding itself:
val foo: ?x:int -> ?y:float-> int -> int
[@@ocaml.deprecated_argument x "Please use y instead!"]
[@@ocaml.deprecated_missing_argument x "Please pass y, it will
soon be mandatory"]
Or indeed allow adding attributes on (labeled?) arguments, or interpret
attributes on their types. But internally, the information could always
be attached to the value binding, which should be simpler than storing
it in the type.
> By deprecating the absence, do you mean having a warning when the argument is ommited?
> Wouldn’t it mean having two distinct deprecation annotations for optional arguments?
Yes, of course. "Old" attributes (that will be discarded at some point)
should be reported when they are passed a value; "new" attributes (that
might become non-optional) should be reported when they are omitted. It
remains to be seen what to do with applications with the ?x syntax (i.e.
passing an option).
Alain
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-07 8:36 ` Alain Frisch
@ 2018-09-07 10:13 ` Jacques Garrigue
2018-09-07 12:49 ` Alain Frisch
0 siblings, 1 reply; 12+ messages in thread
From: Jacques Garrigue @ 2018-09-07 10:13 UTC (permalink / raw)
To: Alain Frisch; +Cc: Mailing List OCaml
On 2018/09/07 17:36, Alain Frisch wrote:
>
> On 07/09/2018 01:45, Jacques Garrigue wrote:
>> Interesting idea.
>> This should be easily doable, for instance by extending the Asttypes.arg_label type.
>
> One could also just keep the information on the value binding itself:
>
> val foo: ?x:int -> ?y:float-> int -> int
> [@@ocaml.deprecated_argument x "Please use y instead!"]
> [@@ocaml.deprecated_missing_argument x "Please pass y, it will soon be mandatory"]
>
>
> Or indeed allow adding attributes on (labeled?) arguments, or interpret attributes on their types. But internally, the information could always be attached to the value binding, which should be simpler than storing it in the type.
This is not so clear.
The binding information could be hard to bring to the application type inference, while if it is contained in the label, it is already available where it is needed.
Also, attaching the deprecation to the binding means that we can only refer to the first optional argument with the same name. In theory, there could be several, but I agree that this is going to be extremely rare.
>> By deprecating the absence, do you mean having a warning when the argument is ommited?
>> Wouldn’t it mean having two distinct deprecation annotations for optional arguments?
>
> Yes, of course. "Old" attributes (that will be discarded at some point) should be reported when they are passed a value; "new" attributes (that might become non-optional) should be reported when they are omitted. It remains to be seen what to do with applications with the ?x syntax (i.e. passing an option).
I suppose ?x should raise a warning too, since it is intended to allow both pasing and not passing, and one of the two is wrong.
Jacques
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] How to rename a record field
2018-09-07 10:13 ` Jacques Garrigue
@ 2018-09-07 12:49 ` Alain Frisch
0 siblings, 0 replies; 12+ messages in thread
From: Alain Frisch @ 2018-09-07 12:49 UTC (permalink / raw)
To: Jacques Garrigue; +Cc: Mailing List OCaml
On 07/09/2018 12:13, Jacques Garrigue wrote:
> On 2018/09/07 17:36, Alain Frisch wrote:
>> Or indeed allow adding attributes on (labeled?) arguments, or interpret attributes on their types. But internally, the information could always be attached to the value binding, which should be simpler than storing it in the type.
>
> This is not so clear.
> The binding information could be hard to bring to the application type inference, while if it is contained in the label, it is already available where it is needed.
Do you have in mind really tracking the annotation during type
inference? For instance, to support binding a partial application of a
function, and still track the deprecation of arguments of the resulting
function?
I was thinking about a much more limited approach: the deprecation of
arguments is checked when the functional value is referenced and
immediately applied.
Quick POC to track the discussion on this topic:
https://github.com/ocaml/ocaml/pull/2027
Alain
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2018-09-07 12:50 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-06 11:36 [Caml-list] How to rename a record field Enrico Tassi
2018-09-06 11:48 ` Gabriel Scherer
2018-09-06 13:03 ` Enrico Tassi
2018-09-06 13:17 ` Jacques Garrigue
2018-09-06 14:18 ` Enrico Tassi
2018-09-06 23:34 ` Jacques Garrigue
2018-09-06 16:20 ` Alain Frisch
2018-09-06 16:21 ` Alain Frisch
2018-09-06 23:45 ` Jacques Garrigue
2018-09-07 8:36 ` Alain Frisch
2018-09-07 10:13 ` Jacques Garrigue
2018-09-07 12:49 ` Alain Frisch
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox