We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
bug: regression: slow to compute name of deeply nested expressions
### What happened?

```python
import ibis

x = ibis.literal(1)
for i in range(100):
    x = x + 1
    # if I hardcode the name, this is instantaneous
    # x = x.name(f"step_{i}")
x
```

If I ctrl-c this after a few seconds, I see see eg

```
File ~/code/ibis/ibis/expr/operations/core.py:89, in Value.name(self)
     86 @property
     87 def name(self) -> str:
     88     names = (arg.name for arg in self.__args__ if hasattr(arg, "name"))
---> 89     return f"{self.__class__.__name__}({', '.join(names)})"

File ~/code/ibis/ibis/expr/operations/core.py:88, in <genexpr>(.0)
     86 @property
     87 def name(self) -> str:
---> 88     names = (arg.name for arg in self.__args__ if hasattr(arg, "name"))
     89     return f"{self.__class__.__name__}({', '.join(names)})"

    [... skipping similar frames: <genexpr> at line 88 (97 times), Value.name at line 89 (97 times)]

File ~/code/ibis/ibis/expr/operations/core.py:89, in Value.name(self)
     86 @property
     87 def name(self) -> str:
     88     names = (arg.name for arg in self.__args__ if hasattr(arg, "name"))
---> 89     return f"{self.__class__.__name__}({', '.join(names)})"

File ~/code/ibis/ibis/expr/operations/core.py:88, in <genexpr>(.0)
     86 @property
     87 def name(self) -> str:
---> 88     names = (arg.name for arg in self.__args__ if hasattr(arg, "name"))
     89     return f"{self.__class__.__name__}({', '.join(names)})"
```

### What version of ibis are you using?

main 09b6adaeecf1b6388866856c795022cfca4b2679

### What backend(s) are you using, if any?

NA

### Relevant log output

_No response_

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ibis/expr/operations/core.py`
Content:
```
1 from __future__ import annotations
2 
3 from abc import abstractmethod
4 from typing import Generic, Optional
5 
6 from public import public
7 from typing_extensions import Any, Self, TypeVar
8 
9 import ibis.expr.datashape as ds
10 import ibis.expr.datatypes as dt
11 import ibis.expr.rules as rlz
12 from ibis import util
13 from ibis.common.annotations import attribute
14 from ibis.common.graph import Node as Traversable
15 from ibis.common.grounds import Concrete
16 from ibis.common.patterns import Coercible, CoercionError
17 from ibis.common.typing import DefaultTypeVars
18 
19 
20 @public
21 class Node(Concrete, Traversable):
22     def equals(self, other) -> bool:
23         if not isinstance(other, Node):
24             raise TypeError(
25                 f"invalid equality comparison between Node and {type(other)}"
26             )
27         return self.__cached_equals__(other)
28 
29     @util.deprecated(as_of="4.0", instead="remove intermediate .op() calls")
30     def op(self) -> Self:
31         """Make `Node` backwards compatible with code that uses `Expr.op()`."""
32         return self
33 
34     # Avoid custom repr for performance reasons
35     __repr__ = object.__repr__
36 
37     # TODO(kszucs): hidrate the __children__ traversable attribute
38     # @attribute
39     # def __children__(self):
40     #     return super().__children__
41 
42 
43 T = TypeVar("T", bound=dt.DataType, covariant=True)
44 S = TypeVar("S", bound=ds.DataShape, default=ds.Any, covariant=True)
45 
46 
47 @public
48 class Value(Node, Coercible, DefaultTypeVars, Generic[T, S]):
49     @classmethod
50     def __coerce__(
51         cls, value: Any, T: Optional[type] = None, S: Optional[type] = None
52     ) -> Self:
53         # note that S=Shape is unused here since the pattern will check the
54         # shape of the value expression after executing Value.__coerce__()
55         from ibis.expr.operations.generic import NULL, Literal
56         from ibis.expr.types import Expr
57 
58         if isinstance(value, Expr):
59             value = value.op()
60 
61         if isinstance(value, Value):
62             if value == NULL:
63                 # treat the NULL literal the same as None to implicitly cast to
64                 # the requested datatype if any
65                 value = None
66             else:
67                 return value
68 
69         if T is dt.Integer:
70             dtype = dt.infer(int(value))
71         elif T is dt.Floating:
72             dtype = dt.infer(float(value))
73         else:
74             try:
75                 dtype = dt.DataType.from_typehint(T)
76             except TypeError:
77                 dtype = dt.infer(value)
78 
79         try:
80             return Literal(value, dtype=dtype)
81         except TypeError:
82             raise CoercionError(f"Unable to coerce {value!r} to Value[{T!r}]")
83 
84     # TODO(kszucs): cover it with tests
85     # TODO(kszucs): figure out how to represent not named arguments
86     @property
87     def name(self) -> str:
88         names = (arg.name for arg in self.__args__ if hasattr(arg, "name"))
89         return f"{self.__class__.__name__}({', '.join(names)})"
90 
91     @property
92     @abstractmethod
93     def dtype(self) -> T:
94         """Ibis datatype of the produced value expression.
95 
96         Returns
97         -------
98         dt.DataType
99 
100         """
101 
102     @property
103     @abstractmethod
104     def shape(self) -> S:
105         """Shape of the produced value expression.
106 
107         Possible values are: "scalar" and "columnar"
108 
109         Returns
110         -------
111         ds.Shape
112 
113         """
114 
115     @attribute
116     def relations(self):
117         """Set of relations the value node depends on."""
118         children = (n.relations for n in self.__children__ if isinstance(n, Value))
119         return frozenset().union(*children)
120 
121     @property
122     @util.deprecated(as_of="7.0", instead="use .dtype property instead")
123     def output_dtype(self):
124         return self.dtype
125 
126     @property
127     @util.deprecated(as_of="7.0", instead="use .shape property instead")
128     def output_shape(self):
129         return self.shape
130 
131     def to_expr(self):
132         import ibis.expr.types as ir
133 
134         if self.shape.is_columnar():
135             typename = self.dtype.column
136         else:
137             typename = self.dtype.scalar
138 
139         return getattr(ir, typename)(self)
140 
141 
142 # convenience aliases
143 Scalar = Value[T, ds.Scalar]
144 Column = Value[T, ds.Columnar]
145 
146 
147 @public
148 class Alias(Value):
149     arg: Value
150     name: str
151 
152     shape = rlz.shape_like("arg")
153     dtype = rlz.dtype_like("arg")
154 
155 
156 @public
157 class Unary(Value):
158     """A unary operation."""
159 
160     arg: Value
161 
162     @attribute
163     def shape(self) -> ds.DataShape:
164         return self.arg.shape
165 
166     @attribute
167     def relations(self):
168         return self.arg.relations
169 
170 
171 @public
172 class Binary(Value):
173     """A binary operation."""
174 
175     left: Value
176     right: Value
177 
178     @attribute
179     def shape(self) -> ds.DataShape:
180         return max(self.left.shape, self.right.shape)
181 
182     @attribute
183     def relations(self):
184         return self.left.relations | self.right.relations
185 
186 
187 @public
188 class Argument(Value):
189     name: str
190     shape: ds.DataShape
191     dtype: dt.DataType
192 
193     @attribute
194     def param(self) -> str:
195         return f"__ibis_param_{self.name}__"
196 
197 
198 public(ValueOp=Value, UnaryOp=Unary, BinaryOp=Binary, Scalar=Scalar, Column=Column)
199
```
--- 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/ibis/expr/operations/core.py b/ibis/expr/operations/core.py
--- a/ibis/expr/operations/core.py
+++ b/ibis/expr/operations/core.py
@@ -85,7 +85,11 @@
     # TODO(kszucs): figure out how to represent not named arguments
     @property
     def name(self) -> str:
-        names = (arg.name for arg in self.__args__ if hasattr(arg, "name"))
+        names = (
+            name
+            for arg in self.__args__
+            if (name := getattr(arg, "name", None)) is not None
+        )
         return f"{self.__class__.__name__}({', '.join(names)})"
 
     @property

{"golden_diff": "diff --git a/ibis/expr/operations/core.py b/ibis/expr/operations/core.py\n--- a/ibis/expr/operations/core.py\n+++ b/ibis/expr/operations/core.py\n@@ -85,7 +85,11 @@\n     # TODO(kszucs): figure out how to represent not named arguments\n     @property\n     def name(self) -> str:\n-        names = (arg.name for arg in self.__args__ if hasattr(arg, \"name\"))\n+        names = (\n+            name\n+            for arg in self.__args__\n+            if (name := getattr(arg, \"name\", None)) is not None\n+        )\n         return f\"{self.__class__.__name__}({', '.join(names)})\"\n \n     @property\n", "issue": "bug: regression: slow to compute name of deeply nested expressions\n### What happened?\n\n```python\r\nimport ibis\r\n\r\nx = ibis.literal(1)\r\nfor i in range(100):\r\n    x = x + 1\r\n    # if I hardcode the name, this is instantaneous\r\n    # x = x.name(f\"step_{i}\")\r\nx\r\n```\r\n\r\nIf I ctrl-c this after a few seconds, I see see eg\r\n\r\n```\r\nFile ~/code/ibis/ibis/expr/operations/core.py:89, in Value.name(self)\r\n     86 @property\r\n     87 def name(self) -> str:\r\n     88     names = (arg.name for arg in self.__args__ if hasattr(arg, \"name\"))\r\n---> 89     return f\"{self.__class__.__name__}({', '.join(names)})\"\r\n\r\nFile ~/code/ibis/ibis/expr/operations/core.py:88, in <genexpr>(.0)\r\n     86 @property\r\n     87 def name(self) -> str:\r\n---> 88     names = (arg.name for arg in self.__args__ if hasattr(arg, \"name\"))\r\n     89     return f\"{self.__class__.__name__}({', '.join(names)})\"\r\n\r\n    [... skipping similar frames: <genexpr> at line 88 (97 times), Value.name at line 89 (97 times)]\r\n\r\nFile ~/code/ibis/ibis/expr/operations/core.py:89, in Value.name(self)\r\n     86 @property\r\n     87 def name(self) -> str:\r\n     88     names = (arg.name for arg in self.__args__ if hasattr(arg, \"name\"))\r\n---> 89     return f\"{self.__class__.__name__}({', '.join(names)})\"\r\n\r\nFile ~/code/ibis/ibis/expr/operations/core.py:88, in <genexpr>(.0)\r\n     86 @property\r\n     87 def name(self) -> str:\r\n---> 88     names = (arg.name for arg in self.__args__ if hasattr(arg, \"name\"))\r\n     89     return f\"{self.__class__.__name__}({', '.join(names)})\"\r\n```\n\n### What version of ibis are you using?\n\nmain 09b6adaeecf1b6388866856c795022cfca4b2679\n\n### What backend(s) are you using, if any?\n\nNA\n\n### Relevant log output\n\n_No response_\n\n### Code of Conduct\n\n- [X] I agree to follow this project's Code of Conduct\n", "before_files": [{"content": "from __future__ import annotations\n\nfrom abc import abstractmethod\nfrom typing import Generic, Optional\n\nfrom public import public\nfrom typing_extensions import Any, Self, TypeVar\n\nimport ibis.expr.datashape as ds\nimport ibis.expr.datatypes as dt\nimport ibis.expr.rules as rlz\nfrom ibis import util\nfrom ibis.common.annotations import attribute\nfrom ibis.common.graph import Node as Traversable\nfrom ibis.common.grounds import Concrete\nfrom ibis.common.patterns import Coercible, CoercionError\nfrom ibis.common.typing import DefaultTypeVars\n\n\n@public\nclass Node(Concrete, Traversable):\n    def equals(self, other) -> bool:\n        if not isinstance(other, Node):\n            raise TypeError(\n                f\"invalid equality comparison between Node and {type(other)}\"\n            )\n        return self.__cached_equals__(other)\n\n    @util.deprecated(as_of=\"4.0\", instead=\"remove intermediate .op() calls\")\n    def op(self) -> Self:\n        \"\"\"Make `Node` backwards compatible with code that uses `Expr.op()`.\"\"\"\n        return self\n\n    # Avoid custom repr for performance reasons\n    __repr__ = object.__repr__\n\n    # TODO(kszucs): hidrate the __children__ traversable attribute\n    # @attribute\n    # def __children__(self):\n    #     return super().__children__\n\n\nT = TypeVar(\"T\", bound=dt.DataType, covariant=True)\nS = TypeVar(\"S\", bound=ds.DataShape, default=ds.Any, covariant=True)\n\n\n@public\nclass Value(Node, Coercible, DefaultTypeVars, Generic[T, S]):\n    @classmethod\n    def __coerce__(\n        cls, value: Any, T: Optional[type] = None, S: Optional[type] = None\n    ) -> Self:\n        # note that S=Shape is unused here since the pattern will check the\n        # shape of the value expression after executing Value.__coerce__()\n        from ibis.expr.operations.generic import NULL, Literal\n        from ibis.expr.types import Expr\n\n        if isinstance(value, Expr):\n            value = value.op()\n\n        if isinstance(value, Value):\n            if value == NULL:\n                # treat the NULL literal the same as None to implicitly cast to\n                # the requested datatype if any\n                value = None\n            else:\n                return value\n\n        if T is dt.Integer:\n            dtype = dt.infer(int(value))\n        elif T is dt.Floating:\n            dtype = dt.infer(float(value))\n        else:\n            try:\n                dtype = dt.DataType.from_typehint(T)\n            except TypeError:\n                dtype = dt.infer(value)\n\n        try:\n            return Literal(value, dtype=dtype)\n        except TypeError:\n            raise CoercionError(f\"Unable to coerce {value!r} to Value[{T!r}]\")\n\n    # TODO(kszucs): cover it with tests\n    # TODO(kszucs): figure out how to represent not named arguments\n    @property\n    def name(self) -> str:\n        names = (arg.name for arg in self.__args__ if hasattr(arg, \"name\"))\n        return f\"{self.__class__.__name__}({', '.join(names)})\"\n\n    @property\n    @abstractmethod\n    def dtype(self) -> T:\n        \"\"\"Ibis datatype of the produced value expression.\n\n        Returns\n        -------\n        dt.DataType\n\n        \"\"\"\n\n    @property\n    @abstractmethod\n    def shape(self) -> S:\n        \"\"\"Shape of the produced value expression.\n\n        Possible values are: \"scalar\" and \"columnar\"\n\n        Returns\n        -------\n        ds.Shape\n\n        \"\"\"\n\n    @attribute\n    def relations(self):\n        \"\"\"Set of relations the value node depends on.\"\"\"\n        children = (n.relations for n in self.__children__ if isinstance(n, Value))\n        return frozenset().union(*children)\n\n    @property\n    @util.deprecated(as_of=\"7.0\", instead=\"use .dtype property instead\")\n    def output_dtype(self):\n        return self.dtype\n\n    @property\n    @util.deprecated(as_of=\"7.0\", instead=\"use .shape property instead\")\n    def output_shape(self):\n        return self.shape\n\n    def to_expr(self):\n        import ibis.expr.types as ir\n\n        if self.shape.is_columnar():\n            typename = self.dtype.column\n        else:\n            typename = self.dtype.scalar\n\n        return getattr(ir, typename)(self)\n\n\n# convenience aliases\nScalar = Value[T, ds.Scalar]\nColumn = Value[T, ds.Columnar]\n\n\n@public\nclass Alias(Value):\n    arg: Value\n    name: str\n\n    shape = rlz.shape_like(\"arg\")\n    dtype = rlz.dtype_like(\"arg\")\n\n\n@public\nclass Unary(Value):\n    \"\"\"A unary operation.\"\"\"\n\n    arg: Value\n\n    @attribute\n    def shape(self) -> ds.DataShape:\n        return self.arg.shape\n\n    @attribute\n    def relations(self):\n        return self.arg.relations\n\n\n@public\nclass Binary(Value):\n    \"\"\"A binary operation.\"\"\"\n\n    left: Value\n    right: Value\n\n    @attribute\n    def shape(self) -> ds.DataShape:\n        return max(self.left.shape, self.right.shape)\n\n    @attribute\n    def relations(self):\n        return self.left.relations | self.right.relations\n\n\n@public\nclass Argument(Value):\n    name: str\n    shape: ds.DataShape\n    dtype: dt.DataType\n\n    @attribute\n    def param(self) -> str:\n        return f\"__ibis_param_{self.name}__\"\n\n\npublic(ValueOp=Value, UnaryOp=Unary, BinaryOp=Binary, Scalar=Scalar, Column=Column)\n", "path": "ibis/expr/operations/core.py"}], "after_files": [{"content": "from __future__ import annotations\n\nfrom abc import abstractmethod\nfrom typing import Generic, Optional\n\nfrom public import public\nfrom typing_extensions import Any, Self, TypeVar\n\nimport ibis.expr.datashape as ds\nimport ibis.expr.datatypes as dt\nimport ibis.expr.rules as rlz\nfrom ibis import util\nfrom ibis.common.annotations import attribute\nfrom ibis.common.graph import Node as Traversable\nfrom ibis.common.grounds import Concrete\nfrom ibis.common.patterns import Coercible, CoercionError\nfrom ibis.common.typing import DefaultTypeVars\n\n\n@public\nclass Node(Concrete, Traversable):\n    def equals(self, other) -> bool:\n        if not isinstance(other, Node):\n            raise TypeError(\n                f\"invalid equality comparison between Node and {type(other)}\"\n            )\n        return self.__cached_equals__(other)\n\n    @util.deprecated(as_of=\"4.0\", instead=\"remove intermediate .op() calls\")\n    def op(self) -> Self:\n        \"\"\"Make `Node` backwards compatible with code that uses `Expr.op()`.\"\"\"\n        return self\n\n    # Avoid custom repr for performance reasons\n    __repr__ = object.__repr__\n\n    # TODO(kszucs): hidrate the __children__ traversable attribute\n    # @attribute\n    # def __children__(self):\n    #     return super().__children__\n\n\nT = TypeVar(\"T\", bound=dt.DataType, covariant=True)\nS = TypeVar(\"S\", bound=ds.DataShape, default=ds.Any, covariant=True)\n\n\n@public\nclass Value(Node, Coercible, DefaultTypeVars, Generic[T, S]):\n    @classmethod\n    def __coerce__(\n        cls, value: Any, T: Optional[type] = None, S: Optional[type] = None\n    ) -> Self:\n        # note that S=Shape is unused here since the pattern will check the\n        # shape of the value expression after executing Value.__coerce__()\n        from ibis.expr.operations.generic import NULL, Literal\n        from ibis.expr.types import Expr\n\n        if isinstance(value, Expr):\n            value = value.op()\n\n        if isinstance(value, Value):\n            if value == NULL:\n                # treat the NULL literal the same as None to implicitly cast to\n                # the requested datatype if any\n                value = None\n            else:\n                return value\n\n        if T is dt.Integer:\n            dtype = dt.infer(int(value))\n        elif T is dt.Floating:\n            dtype = dt.infer(float(value))\n        else:\n            try:\n                dtype = dt.DataType.from_typehint(T)\n            except TypeError:\n                dtype = dt.infer(value)\n\n        try:\n            return Literal(value, dtype=dtype)\n        except TypeError:\n            raise CoercionError(f\"Unable to coerce {value!r} to Value[{T!r}]\")\n\n    # TODO(kszucs): cover it with tests\n    # TODO(kszucs): figure out how to represent not named arguments\n    @property\n    def name(self) -> str:\n        names = (\n            name\n            for arg in self.__args__\n            if (name := getattr(arg, \"name\", None)) is not None\n        )\n        return f\"{self.__class__.__name__}({', '.join(names)})\"\n\n    @property\n    @abstractmethod\n    def dtype(self) -> T:\n        \"\"\"Ibis datatype of the produced value expression.\n\n        Returns\n        -------\n        dt.DataType\n\n        \"\"\"\n\n    @property\n    @abstractmethod\n    def shape(self) -> S:\n        \"\"\"Shape of the produced value expression.\n\n        Possible values are: \"scalar\" and \"columnar\"\n\n        Returns\n        -------\n        ds.Shape\n\n        \"\"\"\n\n    @attribute\n    def relations(self):\n        \"\"\"Set of relations the value node depends on.\"\"\"\n        children = (n.relations for n in self.__children__ if isinstance(n, Value))\n        return frozenset().union(*children)\n\n    @property\n    @util.deprecated(as_of=\"7.0\", instead=\"use .dtype property instead\")\n    def output_dtype(self):\n        return self.dtype\n\n    @property\n    @util.deprecated(as_of=\"7.0\", instead=\"use .shape property instead\")\n    def output_shape(self):\n        return self.shape\n\n    def to_expr(self):\n        import ibis.expr.types as ir\n\n        if self.shape.is_columnar():\n            typename = self.dtype.column\n        else:\n            typename = self.dtype.scalar\n\n        return getattr(ir, typename)(self)\n\n\n# convenience aliases\nScalar = Value[T, ds.Scalar]\nColumn = Value[T, ds.Columnar]\n\n\n@public\nclass Alias(Value):\n    arg: Value\n    name: str\n\n    shape = rlz.shape_like(\"arg\")\n    dtype = rlz.dtype_like(\"arg\")\n\n\n@public\nclass Unary(Value):\n    \"\"\"A unary operation.\"\"\"\n\n    arg: Value\n\n    @attribute\n    def shape(self) -> ds.DataShape:\n        return self.arg.shape\n\n    @attribute\n    def relations(self):\n        return self.arg.relations\n\n\n@public\nclass Binary(Value):\n    \"\"\"A binary operation.\"\"\"\n\n    left: Value\n    right: Value\n\n    @attribute\n    def shape(self) -> ds.DataShape:\n        return max(self.left.shape, self.right.shape)\n\n    @attribute\n    def relations(self):\n        return self.left.relations | self.right.relations\n\n\n@public\nclass Argument(Value):\n    name: str\n    shape: ds.DataShape\n    dtype: dt.DataType\n\n    @attribute\n    def param(self) -> str:\n        return f\"__ibis_param_{self.name}__\"\n\n\npublic(ValueOp=Value, UnaryOp=Unary, BinaryOp=Binary, Scalar=Scalar, Column=Column)\n", "path": "ibis/expr/operations/core.py"}]}