diff mbox series

[v2,5/6] trailer: rename *_DEFAULT enums to *_UNSPECIFIED

Message ID 52958c3557c34992df59e9c10f098f457526702c.1694240177.git.gitgitgadget@gmail.com (mailing list archive)
State Superseded
Commit d2be104085b24867e3dd9cb061c75b456071b351
Headers show
Series Trailer readability cleanups | expand

Commit Message

Linus Arver Sept. 9, 2023, 6:16 a.m. UTC
From: Linus Arver <linusa@google.com>

Do not use *_DEFAULT as a suffix to the enums, because the word
"default" is overloaded. The following are two examples of the ambiguity
of the word "default":

(1) "Default" can mean using the "default" values that are hardcoded
    in trailer.c as

        default_conf_info.where = WHERE_END;
        default_conf_info.if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
        default_conf_info.if_missing = MISSING_ADD;

    in ensure_configured(). These values are referred to as "the
    default" in the docs for interpret-trailers. These defaults are used
    if no "trailer.*" configurations are defined.

(2) "Default" can also mean the "trailer.*" configurations themselves,
    because these configurations are used by "default" (ahead of the
    hardcoded defaults in (1)) if no command line arguments are
    provided. This concept of defaulting back to the configurations was
    introduced in 0ea5292e6b (interpret-trailers: add options for
    actions, 2017-08-01).

In addition, the corresponding *_DEFAULT values are chosen when the user
provides the "--no-where", "--no-if-exists", or "--no-if-missing" flags
on the command line. These "--no-*" flags are used to clear previously
provided flags of the form "--where", "--if-exists", and "--if-missing".
Using these "--no-*" flags undoes the specifying of these flags (if
any), so using the word "UNSPECIFIED" is more natural here.

So instead of using "*_DEFAULT", use "*_UNSPECIFIED" because this
signals to the reader that the *_UNSPECIFIED value by itself carries no
meaning (it's a zero value and by itself does not "default" to anything,
necessitating the need to have some other way of getting to a useful
value).

Signed-off-by: Linus Arver <linusa@google.com>
---
 trailer.c | 17 ++++++++++-------
 trailer.h |  6 +++---
 2 files changed, 13 insertions(+), 10 deletions(-)

Comments

Junio C Hamano Sept. 11, 2023, 6:54 p.m. UTC | #1
"Linus Arver via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Linus Arver <linusa@google.com>
>
> Do not use *_DEFAULT as a suffix to the enums, because the word
> "default" is overloaded. The following are two examples of the ambiguity
> of the word "default":

In this case these are left unspecified to use the default; while it
is not wrong per-se to say *_DEFAULT, using *_UNSPECIFIED makes it
more obvious.

> So instead of using "*_DEFAULT", use "*_UNSPECIFIED" because this
> signals to the reader that the *_UNSPECIFIED value by itself carries no
> meaning (it's a zero value and by itself does not "default" to anything,
> necessitating the need to have some other way of getting to a useful
> value).

It gets tempting to initialize a variable to the default and arrange
the rest of the system so that the variable set to the default
triggers the default activity.  Such an obvious solution however
cannot be used when (1) being left unspecified to use the default
value and (2) explicitly set by the user to a value that happens to
be the same as the default have to behave differently.  I am not
sure if that applies to the trailers system, though.

Thanks.


PS.  Glen's old e-mail address is no longer valid and there is no
forwarding done by @google.com mailservers, it seems.  Can you tell
GGG to drop the address (optionally replace it with his new address)?
Linus Arver Sept. 14, 2023, 2:41 a.m. UTC | #2
Junio C Hamano <gitster@pobox.com> writes:

> "Linus Arver via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
>> From: Linus Arver <linusa@google.com>
>>
>> [...]
>> So instead of using "*_DEFAULT", use "*_UNSPECIFIED" because this
>> signals to the reader that the *_UNSPECIFIED value by itself carries no
>> meaning (it's a zero value and by itself does not "default" to anything,
>> necessitating the need to have some other way of getting to a useful
>> value).
>
> It gets tempting to initialize a variable to the default and arrange
> the rest of the system so that the variable set to the default
> triggers the default activity.  Such an obvious solution however
> cannot be used when (1) being left unspecified to use the default
> value and (2) explicitly set by the user to a value that happens to
> be the same as the default have to behave differently.  I am not
> sure if that applies to the trailers system, though.
>
> Thanks.

I get the feeling that you wrote the "Such an obvious ... differently"
sentence after writing the last sentence in that paragraph, because when
you say

    I am not
    sure if that applies to the trailers system, though.

I read the "that" (emphasis added) in there as referring to the solution
described in the first sentence, and not the conditions (1) and (2) you
enumerated. IOW, you are OK with this patch.

Am I parsing your paragraph correctly?

> PS.  Glen's old e-mail address is no longer valid and there is no
> forwarding done by @google.com mailservers, it seems.  Can you tell
> GGG to drop the address (optionally replace it with his new address)?

Thanks for catching this. I've updated the GGG PR [1] to use Glen's new
address.

[1] https://github.com/gitgitgadget/git/pull/1563#issue-1837440424
Junio C Hamano Sept. 14, 2023, 3:16 a.m. UTC | #3
Linus Arver <linusa@google.com> writes:

>> It gets tempting to initialize a variable to the default and arrange
>> the rest of the system so that the variable set to the default
>> triggers the default activity.  Such an obvious solution however
>> cannot be used when (1) being left unspecified to use the default
>> value and (2) explicitly set by the user to a value that happens to
>> be the same as the default have to behave differently.  I am not
>> sure if that applies to the trailers system, though.
>>
>> Thanks.
>
> I get the feeling that you wrote the "Such an obvious ... differently"
> sentence after writing the last sentence in that paragraph, because when
> you say
>
>     I am not
>     sure if that applies to the trailers system, though.
>
> I read the "that" (emphasis added) in there as referring to the solution
> described in the first sentence, and not the conditions (1) and (2) you
> enumerated. IOW, you are OK with this patch.

"that" refers to "the reason not to use such an obvious solution".
I do not know if trailer subsystem wants to treat "left unspecified"
and "set to the value that happens to be the same as the default" in
a different way.  If it does want to do so, then I do not see a
strong reason not to use the "obvious solution".

Thanks.
Linus Arver Sept. 22, 2023, 6:23 p.m. UTC | #4
Junio C Hamano <gitster@pobox.com> writes:

> Linus Arver <linusa@google.com> writes:
>
>>> It gets tempting to initialize a variable to the default and arrange
>>> the rest of the system so that the variable set to the default
>>> triggers the default activity.  Such an obvious solution however
>>> cannot be used when (1) being left unspecified to use the default
>>> value and (2) explicitly set by the user to a value that happens to
>>> be the same as the default have to behave differently.  I am not
>>> sure if that applies to the trailers system, though.
>>>
>>> Thanks.
>>
>> I get the feeling that you wrote the "Such an obvious ... differently"
>> sentence after writing the last sentence in that paragraph, because when
>> you say
>>
>>     I am not
>>     sure if that applies to the trailers system, though.
>>
>> I read the "that" (emphasis added) in there as referring to the solution
>> described in the first sentence, and not the conditions (1) and (2) you
>> enumerated. IOW, you are OK with this patch.
>
> "that" refers to "the reason not to use such an obvious solution".
> I do not know if trailer subsystem wants to treat "left unspecified"
> and "set to the value that happens to be the same as the default" in
> a different way.  If it does want to do so, then I do not see a
> strong reason not to use the "obvious solution".

Currently we set the defaults (these take effect absent any
configuration or CLI options) in trailer.c like this:

    static void ensure_configured(void)
    {
            if (configured)
                    return;

            /* Default config must be setup first */
            default_conf_info.where = WHERE_END;
            default_conf_info.if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
            default_conf_info.if_missing = MISSING_ADD;
            git_config(git_trailer_default_config, NULL);
            git_config(git_trailer_config, NULL);
            configured = 1;
    }

So technically we already sort of do the "obvious solution". And then
these get overriden by configuration (if any), and finally any CLI
options that are passed in (e.g., "--where after"). The reason why I
prefer the *_UNSPECIFIED style in this patch for these enums though is
because of this (and other similar functions) in trailer.c:

    int trailer_set_where(enum trailer_where *item, const char *value)
    {
            if (!value)
                    *item = WHERE_DEFAULT;
            else if (!strcasecmp("after", value))
                    *item = WHERE_AFTER;
            else if (!strcasecmp("before", value))
                    *item = WHERE_BEFORE;
            else if (!strcasecmp("end", value))
                    *item = WHERE_END;
            else if (!strcasecmp("start", value))
                    *item = WHERE_START;
            else
                    return -1;
            return 0;
    }

and this function is used as a callback to the "--where" flag, such that
the WHERE_DEFAULT gets chosen if "--no-where" is where. I prefer the
WHERE_UNSPECIFIED as in this patch because the WHERE_DEFAULT is
ambiguous on its own (i.e., WHERE_DEFAULT could mean that we either use
the default value WHERE_END in default_conf_info, or it could mean that
we fall back to the configuration variables (where it could be something
else)).
Junio C Hamano Sept. 22, 2023, 7:48 p.m. UTC | #5
Linus Arver <linusa@google.com> writes:

> ... I prefer the
> WHERE_UNSPECIFIED as in this patch because the WHERE_DEFAULT is
> ambiguous on its own (i.e., WHERE_DEFAULT could mean that we either use
> the default value WHERE_END in default_conf_info, or it could mean that
> we fall back to the configuration variables (where it could be something
> else)).

Yup.  "Turning something that is left UNSPECIFIED after command line
options and configuration files are processed into the hardcoded
DEFAULT" is one mental model that is easy to explain.

I however am not sure if it is easier than "Setting something to
hardcoded DEFAULT before command line options and configuration
files are allowed to tweak it, and if nobody touches it, then it
gets the hardcoded DEFAULT value in the end", which is another valid
mental model, though.  If both can be used, I'd personally prefer
the latter, and reserve the "UNSPECIFIED to DEFAULT" pattern to
signal that we are dealing with a case where the simpler pattern
without UNSPECIFIED cannot solve.

The simpler pattern would not work, when the default is defined
depending on a larger context.  Imagine we have two Boolean
variables, A and B, where A defaults to false, and B defaults to
some value derived from the value of A (say, opposite of A).

In the most natural implementation, you'd initialize A to false and
B to unspecified, let command line options and configuration
variables to set them to true or false, and after all that, you do
not have to tweak A's value (it will be left to false that is the
default unless the user or the configuration gave an explicit
value), but you need to check if B is left unspecified and tweak it
to true or false using the final value of A.

For a variable with such a need like B, we cannot avoid having
"unspecified".  If you initialize it to false (or true), after the
command line and the configuration files are read and you find B is
set to false (or true), you cannot tell if the user or the
configuration explicitly set B to false (or true), in which case you
do not want to futz with its value based on what is in A, or it is
false (or true) only because nobody touched it, in which case you
need to compute its value based on what is in A.

And that is why I asked if we need to special case "the user did not
touch and the variable is left untouched" in the trailer subsystem.

Thanks.
Linus Arver Sept. 26, 2023, 5:30 a.m. UTC | #6
Junio C Hamano <gitster@pobox.com> writes:

> Linus Arver <linusa@google.com> writes:
>
>> ... I prefer the
>> WHERE_UNSPECIFIED as in this patch because the WHERE_DEFAULT is
>> ambiguous on its own (i.e., WHERE_DEFAULT could mean that we either use
>> the default value WHERE_END in default_conf_info, or it could mean that
>> we fall back to the configuration variables (where it could be something
>> else)).
>
> Yup.  "Turning something that is left UNSPECIFIED after command line
> options and configuration files are processed into the hardcoded
> DEFAULT" is one mental model that is easy to explain.
>
> I however am not sure if it is easier than "Setting something to
> hardcoded DEFAULT before command line options and configuration
> files are allowed to tweak it, and if nobody touches it, then it
> gets the hardcoded DEFAULT value in the end", which is another valid
> mental model, though.

True.

> If both can be used, I'd personally prefer
> the latter, and reserve the "UNSPECIFIED to DEFAULT" pattern to
> signal that we are dealing with a case where the simpler pattern
> without UNSPECIFIED cannot solve.

SGTM.

> The simpler pattern would not work, when the default is defined
> depending on a larger context.  Imagine we have two Boolean
> variables, A and B, where A defaults to false, and B defaults to
> some value derived from the value of A (say, opposite of A).
>
> In the most natural implementation, you'd initialize A to false and
> B to unspecified, let command line options and configuration
> variables to set them to true or false, and after all that, you do
> not have to tweak A's value (it will be left to false that is the
> default unless the user or the configuration gave an explicit
> value), but you need to check if B is left unspecified and tweak it
> to true or false using the final value of A.
>
> For a variable with such a need like B, we cannot avoid having
> "unspecified".  If you initialize it to false (or true), after the
> command line and the configuration files are read and you find B is
> set to false (or true), you cannot tell if the user or the
> configuration explicitly set B to false (or true), in which case you
> do not want to futz with its value based on what is in A, or it is
> false (or true) only because nobody touched it, in which case you
> need to compute its value based on what is in A.

Thanks for the illustrative example! I don't think we have a case of a
"B" variable here for trailers.

> And that is why I asked if we need to special case "the user did not
> touch and the variable is left untouched" in the trailer subsystem.

I think the answer is "no, we don't need to special case". I'll be
dropping this patch in the next re-roll.

> Thanks.
diff mbox series

Patch

diff --git a/trailer.c b/trailer.c
index f646e484a23..6ad2fbca942 100644
--- a/trailer.c
+++ b/trailer.c
@@ -388,7 +388,7 @@  static void process_trailers_lists(struct list_head *head,
 int trailer_set_where(enum trailer_where *item, const char *value)
 {
 	if (!value)
-		*item = WHERE_DEFAULT;
+		*item = WHERE_UNSPECIFIED;
 	else if (!strcasecmp("after", value))
 		*item = WHERE_AFTER;
 	else if (!strcasecmp("before", value))
@@ -405,7 +405,7 @@  int trailer_set_where(enum trailer_where *item, const char *value)
 int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)
 {
 	if (!value)
-		*item = EXISTS_DEFAULT;
+		*item = EXISTS_UNSPECIFIED;
 	else if (!strcasecmp("addIfDifferent", value))
 		*item = EXISTS_ADD_IF_DIFFERENT;
 	else if (!strcasecmp("addIfDifferentNeighbor", value))
@@ -424,7 +424,7 @@  int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)
 int trailer_set_if_missing(enum trailer_if_missing *item, const char *value)
 {
 	if (!value)
-		*item = MISSING_DEFAULT;
+		*item = MISSING_UNSPECIFIED;
 	else if (!strcasecmp("doNothing", value))
 		*item = MISSING_DO_NOTHING;
 	else if (!strcasecmp("add", value))
@@ -586,7 +586,10 @@  static void ensure_configured(void)
 	if (configured)
 		return;
 
-	/* Default config must be setup first */
+	/*
+	 * Default config must be setup first. These defaults are used if there
+	 * are no "trailer.*" or "trailer.<token>.*" options configured.
+	 */
 	default_conf_info.where = WHERE_END;
 	default_conf_info.if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
 	default_conf_info.if_missing = MISSING_ADD;
@@ -701,11 +704,11 @@  static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
 	new_item->value = val;
 	duplicate_conf(&new_item->conf, conf);
 	if (new_trailer_item) {
-		if (new_trailer_item->where != WHERE_DEFAULT)
+		if (new_trailer_item->where != WHERE_UNSPECIFIED)
 			new_item->conf.where = new_trailer_item->where;
-		if (new_trailer_item->if_exists != EXISTS_DEFAULT)
+		if (new_trailer_item->if_exists != EXISTS_UNSPECIFIED)
 			new_item->conf.if_exists = new_trailer_item->if_exists;
-		if (new_trailer_item->if_missing != MISSING_DEFAULT)
+		if (new_trailer_item->if_missing != MISSING_UNSPECIFIED)
 			new_item->conf.if_missing = new_trailer_item->if_missing;
 	}
 	list_add_tail(&new_item->list, arg_head);
diff --git a/trailer.h b/trailer.h
index ab2cd017567..a689d768c79 100644
--- a/trailer.h
+++ b/trailer.h
@@ -5,14 +5,14 @@ 
 #include "strbuf.h"
 
 enum trailer_where {
-	WHERE_DEFAULT,
+	WHERE_UNSPECIFIED,
 	WHERE_END,
 	WHERE_AFTER,
 	WHERE_BEFORE,
 	WHERE_START
 };
 enum trailer_if_exists {
-	EXISTS_DEFAULT,
+	EXISTS_UNSPECIFIED,
 	EXISTS_ADD_IF_DIFFERENT_NEIGHBOR,
 	EXISTS_ADD_IF_DIFFERENT,
 	EXISTS_ADD,
@@ -20,7 +20,7 @@  enum trailer_if_exists {
 	EXISTS_DO_NOTHING
 };
 enum trailer_if_missing {
-	MISSING_DEFAULT,
+	MISSING_UNSPECIFIED,
 	MISSING_ADD,
 	MISSING_DO_NOTHING
 };