We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Remove the special mangling rule for question marks
This was one mangling detail that I disagreed with @gilch about years ago, but I belatedly think he was right. The rule that `foo?` mangles to `is_foo` (instead of `hyx_fooXquestion_markX`) should probably go. What's convinced me is wanting to use question marks on some names and then deciding not to because of mangling:

- In [Infinitesimal Quest 2 + ε](https://github.com/hylang/simalq), items have a Boolean attribute `destroy-on-pickup` that indicate whether the item is destroyed when it's picked up. I could call it `destroy-on-pickup?`, but that would mangle to the nonsensical `is_destroy_on_pickup`.
- Likewise, `attack?`, meaning "should this monster attack?", would be misleadingly mangled to `is_attack`.

Another concern is that Hy's core macro `is-not` can be written `not?`, which looks like a unary predicate for some sort of object called a "not", instead of a binary function that indicates whether `a` is not `b`.

One motivation for the question-mark rule was that it could provide a Lispy alias for a Python name: if a library has a function `is_foo`, you can call it as `foo`. But this purpose was always undermined by [Python core's inconsistency between `isfoo` and `is_foo`](https://github.com/python/cpython/issues/73274).

Finally, there's something to be said for making mangling a little simpler, since it's pretty complicated.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `hy/reader/mangling.py`
Content:
```
1 import re
2 import unicodedata
3 
4 MANGLE_DELIM = "X"
5 
6 normalizes_to_underscore = "_︳︴﹍﹎﹏＿"
7 
8 
9 def mangle(s):
10     """Stringify the argument (with :class:`str`, not :func:`repr` or
11     :hy:func:`hy.repr`) and convert it to a valid Python identifier according
12     to :ref:`Hy's mangling rules <mangling>`. ::
13 
14         (hy.mangle 'foo-bar?)  ; => "is_foo_bar"
15         (hy.mangle "🦑")       ; => "hyx_squid"
16 
17     If the stringified argument is already both legal as a Python identifier
18     and normalized according to Unicode normalization form KC (NFKC), it will
19     be returned unchanged. Thus, ``hy.mangle`` is idempotent. ::
20 
21         (setv x '♦-->♠)
22         (= (hy.mangle (hy.mangle x)) (hy.mangle x))  ; => True
23 
24     Generally, the stringifed input is expected to be parsable as a symbol. As
25     a convenience, it can also have the syntax of a :ref:`dotted identifier
26     <dotted-identifiers>`, and ``hy.mangle`` will mangle the dot-delimited
27     parts separately. ::
28 
29         (hy.mangle "a.b?.c!.d")  ; => "a.is_b.hyx_cXexclamation_markX.d"
30     """
31 
32     assert s
33     s = str(s)
34 
35     if "." in s and s.strip("."):
36         return ".".join(mangle(x) if x else "" for x in s.split("."))
37 
38     # Step 1: Remove and save leading underscores
39     s2 = s.lstrip(normalizes_to_underscore)
40     leading_underscores = "_" * (len(s) - len(s2))
41     s = s2
42 
43     # Step 2: Convert hyphens without introducing a new leading underscore
44     s = s[0] + s[1:].replace("-", "_") if s else s
45 
46     # Step 3: Convert trailing `?` to leading `is_`
47     if s.endswith("?"):
48         s = "is_" + s[:-1]
49 
50     # Step 4: Convert invalid characters or reserved words
51     if not (leading_underscores + s).isidentifier():
52         # Replace illegal characters with their Unicode character
53         # names, or hexadecimal if they don't have one.
54         s = "hyx_" + "".join(
55             c if c != MANGLE_DELIM and ("S" + c).isidentifier()
56             # We prepend the "S" because some characters aren't
57             # allowed at the start of an identifier.
58             else "{0}{1}{0}".format(
59                 MANGLE_DELIM,
60                 unicodedata.name(c, "").lower().replace("-", "H").replace(" ", "_")
61                 or "U{:x}".format(ord(c)),
62             )
63             for c in s
64         )
65 
66     # Step 5: Add back leading underscores
67     s = leading_underscores + s
68 
69     # Normalize Unicode per PEP 3131.
70     s = unicodedata.normalize("NFKC", s)
71 
72     assert s.isidentifier()
73     return s
74 
75 
76 def unmangle(s):
77     """Stringify the argument and try to convert it to a pretty unmangled
78     form. See :ref:`Hy's mangling rules <mangling>`.
79 
80     Unmangling may not round-trip, because different Hy symbol names can mangle
81     to the same Python identifier. In particular, Python itself already
82     considers distinct strings that have the same normalized form (according to
83     NFKC), such as ``hello`` and ``𝔥𝔢𝔩𝔩𝔬``, to be the same identifier.
84 
85     Examples:
86       ::
87 
88          => (hy.unmangle 'foo_bar)
89          "foo-bar"
90 
91          => (hy.unmangle 'is_foo_bar)
92          "foo-bar?"
93 
94          => (hy.unmangle 'hyx_XasteriskX)
95          "*"
96 
97          => (hy.unmangle '_hyx_is_fooXsolidusXa)
98          "_foo/a?"
99 
100          => (hy.unmangle 'hyx_XhyphenHminusX_XgreaterHthan_signX)
101          "-->"
102 
103          => (hy.unmangle 'hyx_XlessHthan_signX__)
104          "<--"
105 
106          => (hy.unmangle '__dunder_name__)
107          "__dunder-name__"
108 
109     """
110 
111     s = str(s)
112 
113     prefix = ""
114     suffix = ""
115     m = re.fullmatch(r"(_+)(.*?)(_*)", s, re.DOTALL)
116     if m:
117         prefix, s, suffix = m.groups()
118 
119     if s.startswith("hyx_"):
120         s = re.sub(
121             "{0}(U)?([_a-z0-9H]+?){0}".format(MANGLE_DELIM),
122             lambda mo: chr(int(mo.group(2), base=16))
123             if mo.group(1)
124             else unicodedata.lookup(
125                 mo.group(2).replace("_", " ").replace("H", "-").upper()
126             ),
127             s[len("hyx_") :],
128         )
129     if s.startswith("is_"):
130         s = s[len("is_") :] + "?"
131     s = s.replace("_", "-")
132 
133     return prefix + s + suffix
134
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/hy/reader/mangling.py b/hy/reader/mangling.py
--- a/hy/reader/mangling.py
+++ b/hy/reader/mangling.py
@@ -11,7 +11,7 @@
     :hy:func:`hy.repr`) and convert it to a valid Python identifier according
     to :ref:`Hy's mangling rules <mangling>`. ::
 
-        (hy.mangle 'foo-bar?)  ; => "is_foo_bar"
+        (hy.mangle 'foo-bar)   ; => "foo_bar"
         (hy.mangle "🦑")       ; => "hyx_squid"
 
     If the stringified argument is already both legal as a Python identifier
@@ -26,7 +26,7 @@
     <dotted-identifiers>`, and ``hy.mangle`` will mangle the dot-delimited
     parts separately. ::
 
-        (hy.mangle "a.b?.c!.d")  ; => "a.is_b.hyx_cXexclamation_markX.d"
+        (hy.mangle "a.c!.d")  ; => "a.hyx_cXexclamation_markX.d"
     """
 
     assert s
@@ -35,19 +35,15 @@
     if "." in s and s.strip("."):
         return ".".join(mangle(x) if x else "" for x in s.split("."))
 
-    # Step 1: Remove and save leading underscores
+    # Remove and save leading underscores
     s2 = s.lstrip(normalizes_to_underscore)
     leading_underscores = "_" * (len(s) - len(s2))
     s = s2
 
-    # Step 2: Convert hyphens without introducing a new leading underscore
+    # Convert hyphens without introducing a new leading underscore
     s = s[0] + s[1:].replace("-", "_") if s else s
 
-    # Step 3: Convert trailing `?` to leading `is_`
-    if s.endswith("?"):
-        s = "is_" + s[:-1]
-
-    # Step 4: Convert invalid characters or reserved words
+    # Convert invalid characters or reserved words
     if not (leading_underscores + s).isidentifier():
         # Replace illegal characters with their Unicode character
         # names, or hexadecimal if they don't have one.
@@ -88,15 +84,9 @@
          => (hy.unmangle 'foo_bar)
          "foo-bar"
 
-         => (hy.unmangle 'is_foo_bar)
-         "foo-bar?"
-
          => (hy.unmangle 'hyx_XasteriskX)
          "*"
 
-         => (hy.unmangle '_hyx_is_fooXsolidusXa)
-         "_foo/a?"
-
          => (hy.unmangle 'hyx_XhyphenHminusX_XgreaterHthan_signX)
          "-->"
 
@@ -126,8 +116,6 @@
             ),
             s[len("hyx_") :],
         )
-    if s.startswith("is_"):
-        s = s[len("is_") :] + "?"
     s = s.replace("_", "-")
 
     return prefix + s + suffix

{"golden_diff": "diff --git a/hy/reader/mangling.py b/hy/reader/mangling.py\n--- a/hy/reader/mangling.py\n+++ b/hy/reader/mangling.py\n@@ -11,7 +11,7 @@\n     :hy:func:`hy.repr`) and convert it to a valid Python identifier according\n     to :ref:`Hy's mangling rules <mangling>`. ::\n \n-        (hy.mangle 'foo-bar?)  ; => \"is_foo_bar\"\n+        (hy.mangle 'foo-bar)   ; => \"foo_bar\"\n         (hy.mangle \"\ud83e\udd91\")       ; => \"hyx_squid\"\n \n     If the stringified argument is already both legal as a Python identifier\n@@ -26,7 +26,7 @@\n     <dotted-identifiers>`, and ``hy.mangle`` will mangle the dot-delimited\n     parts separately. ::\n \n-        (hy.mangle \"a.b?.c!.d\")  ; => \"a.is_b.hyx_cXexclamation_markX.d\"\n+        (hy.mangle \"a.c!.d\")  ; => \"a.hyx_cXexclamation_markX.d\"\n     \"\"\"\n \n     assert s\n@@ -35,19 +35,15 @@\n     if \".\" in s and s.strip(\".\"):\n         return \".\".join(mangle(x) if x else \"\" for x in s.split(\".\"))\n \n-    # Step 1: Remove and save leading underscores\n+    # Remove and save leading underscores\n     s2 = s.lstrip(normalizes_to_underscore)\n     leading_underscores = \"_\" * (len(s) - len(s2))\n     s = s2\n \n-    # Step 2: Convert hyphens without introducing a new leading underscore\n+    # Convert hyphens without introducing a new leading underscore\n     s = s[0] + s[1:].replace(\"-\", \"_\") if s else s\n \n-    # Step 3: Convert trailing `?` to leading `is_`\n-    if s.endswith(\"?\"):\n-        s = \"is_\" + s[:-1]\n-\n-    # Step 4: Convert invalid characters or reserved words\n+    # Convert invalid characters or reserved words\n     if not (leading_underscores + s).isidentifier():\n         # Replace illegal characters with their Unicode character\n         # names, or hexadecimal if they don't have one.\n@@ -88,15 +84,9 @@\n          => (hy.unmangle 'foo_bar)\n          \"foo-bar\"\n \n-         => (hy.unmangle 'is_foo_bar)\n-         \"foo-bar?\"\n-\n          => (hy.unmangle 'hyx_XasteriskX)\n          \"*\"\n \n-         => (hy.unmangle '_hyx_is_fooXsolidusXa)\n-         \"_foo/a?\"\n-\n          => (hy.unmangle 'hyx_XhyphenHminusX_XgreaterHthan_signX)\n          \"-->\"\n \n@@ -126,8 +116,6 @@\n             ),\n             s[len(\"hyx_\") :],\n         )\n-    if s.startswith(\"is_\"):\n-        s = s[len(\"is_\") :] + \"?\"\n     s = s.replace(\"_\", \"-\")\n \n     return prefix + s + suffix\n", "issue": "Remove the special mangling rule for question marks\nThis was one mangling detail that I disagreed with @gilch about years ago, but I belatedly think he was right. The rule that `foo?` mangles to `is_foo` (instead of `hyx_fooXquestion_markX`) should probably go. What's convinced me is wanting to use question marks on some names and then deciding not to because of mangling:\r\n\r\n- In [Infinitesimal Quest 2 + \u03b5](https://github.com/hylang/simalq), items have a Boolean attribute `destroy-on-pickup` that indicate whether the item is destroyed when it's picked up. I could call it `destroy-on-pickup?`, but that would mangle to the nonsensical `is_destroy_on_pickup`.\r\n- Likewise, `attack?`, meaning \"should this monster attack?\", would be misleadingly mangled to `is_attack`.\r\n\r\nAnother concern is that Hy's core macro `is-not` can be written `not?`, which looks like a unary predicate for some sort of object called a \"not\", instead of a binary function that indicates whether `a` is not `b`.\r\n\r\nOne motivation for the question-mark rule was that it could provide a Lispy alias for a Python name: if a library has a function `is_foo`, you can call it as `foo`. But this purpose was always undermined by [Python core's inconsistency between `isfoo` and `is_foo`](https://github.com/python/cpython/issues/73274).\r\n\r\nFinally, there's something to be said for making mangling a little simpler, since it's pretty complicated.\n", "before_files": [{"content": "import re\nimport unicodedata\n\nMANGLE_DELIM = \"X\"\n\nnormalizes_to_underscore = \"_\ufe33\ufe34\ufe4d\ufe4e\ufe4f\uff3f\"\n\n\ndef mangle(s):\n    \"\"\"Stringify the argument (with :class:`str`, not :func:`repr` or\n    :hy:func:`hy.repr`) and convert it to a valid Python identifier according\n    to :ref:`Hy's mangling rules <mangling>`. ::\n\n        (hy.mangle 'foo-bar?)  ; => \"is_foo_bar\"\n        (hy.mangle \"\ud83e\udd91\")       ; => \"hyx_squid\"\n\n    If the stringified argument is already both legal as a Python identifier\n    and normalized according to Unicode normalization form KC (NFKC), it will\n    be returned unchanged. Thus, ``hy.mangle`` is idempotent. ::\n\n        (setv x '\u2666-->\u2660)\n        (= (hy.mangle (hy.mangle x)) (hy.mangle x))  ; => True\n\n    Generally, the stringifed input is expected to be parsable as a symbol. As\n    a convenience, it can also have the syntax of a :ref:`dotted identifier\n    <dotted-identifiers>`, and ``hy.mangle`` will mangle the dot-delimited\n    parts separately. ::\n\n        (hy.mangle \"a.b?.c!.d\")  ; => \"a.is_b.hyx_cXexclamation_markX.d\"\n    \"\"\"\n\n    assert s\n    s = str(s)\n\n    if \".\" in s and s.strip(\".\"):\n        return \".\".join(mangle(x) if x else \"\" for x in s.split(\".\"))\n\n    # Step 1: Remove and save leading underscores\n    s2 = s.lstrip(normalizes_to_underscore)\n    leading_underscores = \"_\" * (len(s) - len(s2))\n    s = s2\n\n    # Step 2: Convert hyphens without introducing a new leading underscore\n    s = s[0] + s[1:].replace(\"-\", \"_\") if s else s\n\n    # Step 3: Convert trailing `?` to leading `is_`\n    if s.endswith(\"?\"):\n        s = \"is_\" + s[:-1]\n\n    # Step 4: Convert invalid characters or reserved words\n    if not (leading_underscores + s).isidentifier():\n        # Replace illegal characters with their Unicode character\n        # names, or hexadecimal if they don't have one.\n        s = \"hyx_\" + \"\".join(\n            c if c != MANGLE_DELIM and (\"S\" + c).isidentifier()\n            # We prepend the \"S\" because some characters aren't\n            # allowed at the start of an identifier.\n            else \"{0}{1}{0}\".format(\n                MANGLE_DELIM,\n                unicodedata.name(c, \"\").lower().replace(\"-\", \"H\").replace(\" \", \"_\")\n                or \"U{:x}\".format(ord(c)),\n            )\n            for c in s\n        )\n\n    # Step 5: Add back leading underscores\n    s = leading_underscores + s\n\n    # Normalize Unicode per PEP 3131.\n    s = unicodedata.normalize(\"NFKC\", s)\n\n    assert s.isidentifier()\n    return s\n\n\ndef unmangle(s):\n    \"\"\"Stringify the argument and try to convert it to a pretty unmangled\n    form. See :ref:`Hy's mangling rules <mangling>`.\n\n    Unmangling may not round-trip, because different Hy symbol names can mangle\n    to the same Python identifier. In particular, Python itself already\n    considers distinct strings that have the same normalized form (according to\n    NFKC), such as ``hello`` and ``\ud835\udd25\ud835\udd22\ud835\udd29\ud835\udd29\ud835\udd2c``, to be the same identifier.\n\n    Examples:\n      ::\n\n         => (hy.unmangle 'foo_bar)\n         \"foo-bar\"\n\n         => (hy.unmangle 'is_foo_bar)\n         \"foo-bar?\"\n\n         => (hy.unmangle 'hyx_XasteriskX)\n         \"*\"\n\n         => (hy.unmangle '_hyx_is_fooXsolidusXa)\n         \"_foo/a?\"\n\n         => (hy.unmangle 'hyx_XhyphenHminusX_XgreaterHthan_signX)\n         \"-->\"\n\n         => (hy.unmangle 'hyx_XlessHthan_signX__)\n         \"<--\"\n\n         => (hy.unmangle '__dunder_name__)\n         \"__dunder-name__\"\n\n    \"\"\"\n\n    s = str(s)\n\n    prefix = \"\"\n    suffix = \"\"\n    m = re.fullmatch(r\"(_+)(.*?)(_*)\", s, re.DOTALL)\n    if m:\n        prefix, s, suffix = m.groups()\n\n    if s.startswith(\"hyx_\"):\n        s = re.sub(\n            \"{0}(U)?([_a-z0-9H]+?){0}\".format(MANGLE_DELIM),\n            lambda mo: chr(int(mo.group(2), base=16))\n            if mo.group(1)\n            else unicodedata.lookup(\n                mo.group(2).replace(\"_\", \" \").replace(\"H\", \"-\").upper()\n            ),\n            s[len(\"hyx_\") :],\n        )\n    if s.startswith(\"is_\"):\n        s = s[len(\"is_\") :] + \"?\"\n    s = s.replace(\"_\", \"-\")\n\n    return prefix + s + suffix\n", "path": "hy/reader/mangling.py"}], "after_files": [{"content": "import re\nimport unicodedata\n\nMANGLE_DELIM = \"X\"\n\nnormalizes_to_underscore = \"_\ufe33\ufe34\ufe4d\ufe4e\ufe4f\uff3f\"\n\n\ndef mangle(s):\n    \"\"\"Stringify the argument (with :class:`str`, not :func:`repr` or\n    :hy:func:`hy.repr`) and convert it to a valid Python identifier according\n    to :ref:`Hy's mangling rules <mangling>`. ::\n\n        (hy.mangle 'foo-bar)   ; => \"foo_bar\"\n        (hy.mangle \"\ud83e\udd91\")       ; => \"hyx_squid\"\n\n    If the stringified argument is already both legal as a Python identifier\n    and normalized according to Unicode normalization form KC (NFKC), it will\n    be returned unchanged. Thus, ``hy.mangle`` is idempotent. ::\n\n        (setv x '\u2666-->\u2660)\n        (= (hy.mangle (hy.mangle x)) (hy.mangle x))  ; => True\n\n    Generally, the stringifed input is expected to be parsable as a symbol. As\n    a convenience, it can also have the syntax of a :ref:`dotted identifier\n    <dotted-identifiers>`, and ``hy.mangle`` will mangle the dot-delimited\n    parts separately. ::\n\n        (hy.mangle \"a.c!.d\")  ; => \"a.hyx_cXexclamation_markX.d\"\n    \"\"\"\n\n    assert s\n    s = str(s)\n\n    if \".\" in s and s.strip(\".\"):\n        return \".\".join(mangle(x) if x else \"\" for x in s.split(\".\"))\n\n    # Remove and save leading underscores\n    s2 = s.lstrip(normalizes_to_underscore)\n    leading_underscores = \"_\" * (len(s) - len(s2))\n    s = s2\n\n    # Convert hyphens without introducing a new leading underscore\n    s = s[0] + s[1:].replace(\"-\", \"_\") if s else s\n\n    # Convert invalid characters or reserved words\n    if not (leading_underscores + s).isidentifier():\n        # Replace illegal characters with their Unicode character\n        # names, or hexadecimal if they don't have one.\n        s = \"hyx_\" + \"\".join(\n            c if c != MANGLE_DELIM and (\"S\" + c).isidentifier()\n            # We prepend the \"S\" because some characters aren't\n            # allowed at the start of an identifier.\n            else \"{0}{1}{0}\".format(\n                MANGLE_DELIM,\n                unicodedata.name(c, \"\").lower().replace(\"-\", \"H\").replace(\" \", \"_\")\n                or \"U{:x}\".format(ord(c)),\n            )\n            for c in s\n        )\n\n    # Step 5: Add back leading underscores\n    s = leading_underscores + s\n\n    # Normalize Unicode per PEP 3131.\n    s = unicodedata.normalize(\"NFKC\", s)\n\n    assert s.isidentifier()\n    return s\n\n\ndef unmangle(s):\n    \"\"\"Stringify the argument and try to convert it to a pretty unmangled\n    form. See :ref:`Hy's mangling rules <mangling>`.\n\n    Unmangling may not round-trip, because different Hy symbol names can mangle\n    to the same Python identifier. In particular, Python itself already\n    considers distinct strings that have the same normalized form (according to\n    NFKC), such as ``hello`` and ``\ud835\udd25\ud835\udd22\ud835\udd29\ud835\udd29\ud835\udd2c``, to be the same identifier.\n\n    Examples:\n      ::\n\n         => (hy.unmangle 'foo_bar)\n         \"foo-bar\"\n\n         => (hy.unmangle 'hyx_XasteriskX)\n         \"*\"\n\n         => (hy.unmangle 'hyx_XhyphenHminusX_XgreaterHthan_signX)\n         \"-->\"\n\n         => (hy.unmangle 'hyx_XlessHthan_signX__)\n         \"<--\"\n\n         => (hy.unmangle '__dunder_name__)\n         \"__dunder-name__\"\n\n    \"\"\"\n\n    s = str(s)\n\n    prefix = \"\"\n    suffix = \"\"\n    m = re.fullmatch(r\"(_+)(.*?)(_*)\", s, re.DOTALL)\n    if m:\n        prefix, s, suffix = m.groups()\n\n    if s.startswith(\"hyx_\"):\n        s = re.sub(\n            \"{0}(U)?([_a-z0-9H]+?){0}\".format(MANGLE_DELIM),\n            lambda mo: chr(int(mo.group(2), base=16))\n            if mo.group(1)\n            else unicodedata.lookup(\n                mo.group(2).replace(\"_\", \" \").replace(\"H\", \"-\").upper()\n            ),\n            s[len(\"hyx_\") :],\n        )\n    s = s.replace(\"_\", \"-\")\n\n    return prefix + s + suffix\n", "path": "hy/reader/mangling.py"}]}