Python and TypeScript¶
The Python package and the TypeScript package are one design in two idioms. Both are thin views over the same runtime: a generated object holds one Viper value and delegates every operation to it, so what an operation does is the runtime’s in both languages. What differs is how each language spells it. The two surfaces stay parallel: a feature added to one is added to the other in the same change.
Both follow the C++ surface, the pack’s base reference: they offer what it offers, with its restrictions (see kibo-template-viper).
Where the idioms differ¶
Concern |
Python |
TypeScript |
|---|---|---|
Viper value |
|
|
construct |
|
|
structure field |
|
get/set accessors |
enumeration |
|
literal union + object: |
container |
|
same views, |
absent optional |
|
|
equality, hash, order, display |
|
|
container protocol |
|
|
a value of another type |
|
|
key conversion |
|
|
an unknown case name ( |
|
|
a variant read as an alternative it does not hold ( |
|
|
a native number stored in an |
an |
a |
documentation |
the DSM documentation on the class, the field, the attachment |
the same places; a structure’s, on the class, not on its |
attachment |
|
|
attachment verbs |
|
|
field-level verbs |
|
|
function pool |
|
|
The argument order is the same everywhere: state first, then key, then value.
Why the rows are idioms¶
Each row is the same meaning written in each language’s grammar, never a different meaning.
Python spells a field in snake_case and TypeScript keeps the model’s name; Python has None
where TypeScript has undefined; Python’s del is a keyword, so an attachment deletes with
delete there and with del in TypeScript, as in C++; Python reaches a container through its
dunder protocol (len(), in, +), TypeScript through Symbol.iterator and methods; Python
hashes with hash(), TypeScript gives hashKey(), the key a native Map or Set needs.
The error rows follow the three layers both pages describe — an argument of another kind,
content the runtime refuses, a read — and each language’s own name for the last one. A case
name is a lookup: Python’s enum answers a failed lookup with ValueError, while
TypeScript has no such convention and passes on the runtime’s ViperError. A variant read
as the wrong alternative is a read of a value of another type: Python names it
ValueError, as it does for a value it cannot take, and TypeScript TypeError. The any
row is each language’s number type: Python’s int and float are two types, and the
runtime keeps the difference; a JavaScript number is always a double, and an integer
reaches an any as a bigint, as int64 fields take it.
The line between idiom and drift is the runtime. A difference that lives in the static surface
and follows the host language — an enum.Enum against a literal union, del against
delete — is legitimate. A difference that changes what happens at run time — another
operation, another argument order, a value that is not the runtime’s — is a bug, whatever the
language. Divergences beyond the rows above are drift, not idiom.
One row records a gap rather than an idiom: a function pool. Python renders a local Pool
beside Remote; TypeScript renders the client side, Remote, only.
How the parity is checked¶
The rows were measured, not recalled. The pack’s test laboratory,
devkit-codegen-test, renders the
Python and TypeScript packages of each of its sites and lists every public member of both —
the classes and their members, an attachment’s operations, the containers, the runtime the
package re-exports. It compares them by name, case and underscores aside (diff_keys is
diffKeys, f_e is f_E), together with whether each member carries documentation.
A difference is either one of the idioms above, which the laboratory holds as data with its
reason, or drift: a member one language has and the other lacks, or documents and the other
does not. Drift fails the laboratory’s check (check.py, through tools/parity.py). An idiom
is added there only together with its row in the table above.