A program is a grid. Down one side are the cases it has to handle; along the other are the things it has to do with them. Every cell is one expression, and a language does not get to decide what goes in them — only how they are boxed up. Objects draw the boxes across the rows. Functions draw them down the columns. Same grid, same cells, same answers; two arrangements, and each one is easy to extend in exactly the direction the other is hard.
That is the expression problem, and the table is the whole of it. Neither arrangement is behind the other: one is cheap in the direction the other is dear, and the numbers cross over as the grid grows.
Generated from the cells above, not written out twice. Press the other arrangement and every character in the boxes stays the same; only the scaffolding around them moves.
by case
by operation
The class knows its own methods, so the lookup is one hop whatever the case is. The match has to ask, and it asks its arms in order, so a case near the bottom of the list costs more questions than one near the top — walk a triangle and then walk a circle. Both roads end on the same line.
The Python is read rather than mimed.
python.js
is a small one written for this page — a lexer, a Pratt parser over the
expression grammar a cell is allowed, and an evaluator that keeps ints and
floats apart the way Python does, so b * h / 2 comes back
6.0 and not 6. The same token stream that runs a
cell is the one that colours it, so a thing painted as a number is a thing
the evaluator read as a number.
selftest.js
checks it against CPython's own answers, in the browser at
tools/verify and under
node with node expression-problem/selftest.js.
Only expressions, which is all a cell is ever allowed to be: names,
attributes, calls, numbers, strings and f-strings, the arithmetic and
comparison operators, and not, and,
or. No statements, no lambdas, no comprehensions. A cell that
reaches past that gets a parse error where its answer would be, which is the
honest report. Transcendental functions come from the browser's own library
rather than CPython's, and the two are entitled to differ in the last bit.
The cells are yours to edit. Anything you type is an expression over that
row's own field names, which is why one cell can serve both arrangements
without being rewritten: the class binds those names off self,
the match binds them in its pattern, and the expression in between never
learns which happened.