Myna (myna-lang)
Myna is an ultra-dense, symbol-based, AI-native programming language designed to cut LLM token and character usage by ~50% while transpiling 1:1 to Python 3.12+ and to bounded JavaScript (ES2024+) and TypeScript targets—and decompiling existing Python code back into Myna.
Why Myna?
Coding agents spend thousands of context and output tokens on repetitive syntactic boilerplate (def __init__(self, ...):, self., Optional[...], try ... except Exception as e:, quoted JSON keys, and 4-space indentation blocks).
Myna replaces verbose multi-token boilerplate with tokenizer-friendly ASCII sigils and digraphs ($$, @, ~@, =>, ??, !?, !&, $, $?) and brace-delimited blocks ({}):
- ~46%–52% fewer LLM tokens and ~48%–58% fewer characters than equivalent Python (compact and ultra-compact modes).
- Zero indentation fragility: Blocks use
{ ... }or=>, making partial diffs and single-line generation rock-solid for AI agents. - Bi-directional (
Python <-> Myna) & Multi-Target (Myna -> Python / JS / TS): Compress existing Python codebases into.mynato fit twice as much code into an agent's context window, or let agents write.mynaand transpile to clean Python, JavaScript, or typed TypeScript. - Agent-Ready: Ships with a compact Rosetta Stone skill (
skills/myna/SKILL.md) that teaches any coding agent to read and write idiomatic Myna in one shot.
Side-by-Side Comparison
Python (21 lines, 611 chars)
from dataclasses import dataclass
import asyncio
@dataclass
class Task:
id: int
title: str
priority: int = 1
done: bool = False
def to_dict(self) -> dict[str, object]:
return {"id": self.id, "title": self.title, "done": self.done}
async def pending(tasks: list[Task], min_p: int = 1) -> list[dict[str, object]]:
try:
await asyncio.sleep(0.01)
items = [t for t in tasks if not t.done and t.priority >= min_p]
return [t.to_dict() for t in sorted(items, key=lambda t: -t.priority)]
except Exception as e:
print(f"Error: {e}")
return []
Myna (11 lines, 299 chars — 51% smaller)
<asyncio
$$Task(id:i,title:s,priority:i=1,done:b=F){
@to_dict->{s:A}=>{.id,.title,.done}
}
~@pending(tasks:[Task],min_p:i=1)->[{s:A}]{
!{
~asyncio.sleep(0.01)
items=[t|t<-tasks?!t.done&&t.priority>=min_p]
^[t.to_dict()|t<-@srt(items,key=t=>-t.priority)]
}!!e{p(`Error: {e}`);^[]}
}
Quickstart
The CLI requires Python 3.11+ and has zero external dependencies. Generated Python targets 3.12+ when the source declares a type alias ($= emits type X = ...); other output runs on 3.11+.
# Install from PyPI
pip install myna-lang # or: uv tool install myna-lang / pipx install myna-lang
# Install for local development (from a checkout)
pip install -e .
# Show the package and language versions
myna --version
# Create and run a script
printf 'p(`hello from {1+1}`)\n' > hello.myna
myna run hello.myna
# The remaining examples are in a checkout of the repository.
# Run a Myna script directly (via Python)
python3 -m myna run examples/demo.myna
# Run a portable Myna script via Node.js (JavaScript target)
python3 -m myna run -t js examples/cross_target.myna
# Compile Myna -> Python (.py)
python3 -m myna compile examples/rest_api.myna -o rest_api.py
# Compile Myna -> JavaScript (.js)
python3 -m myna compile -t js examples/cross_target.myna -o cross_target.js
# Compile Myna -> TypeScript (.ts), or infer it from the output extension
python3 -m myna compile -t ts examples/cross_target.myna -o cross_target.ts
# Bundle a multi-file program into one standalone Python file
python3 -m myna bundle src/main.myna -o app.py
# ...or into a runnable zipapp (stdlib-only, executable)
python3 -m myna bundle src/main.myna --zipapp -o app.pyz
# Compress existing Python -> Myna (.myna)
python3 -m myna decompile script.py -o script.myna
# Ultra-compact decompilation (with local variable minification)
python3 -m myna decompile -m script.py -o script.min.myna
# Compare token & character efficiency
python3 -m myna stats examples/books_api.myna
# Report declared/inferred Python, JavaScript & TypeScript support
python3 -m myna targets examples/demo.myna
Multi-file programs
myna run gives relative imports a package context, so a program can be split into
sibling .myna files:
# main.myna
<.util:{slug}
p(slug("Hello World"))
# util.myna
@slug(text:s)->s { ^ text.lower().replace(" ", "-") }
-
<.util:{slug}→util.mynabeside the importing module. -
<.pkg.helper:{parse}→pkg/helper.myna;pkgmay have an__init__.mynaor be a plain directory (namespace package). -
<.util(no{...}list) binds the imported module object. -
Absolute imports (
<json,<mypkg>alias) keep their normal Python meaning. -
Imported files are compiled on first import and cached by Python's import system.
-
myna run --module-root DIR(orMYNA_MODULE_ROOT=DIR) resolves the entry file's relative imports fromDIRinstead of the entry's own directory. For the usualsrc/+tests/layout this removes the need for a symlink:python3 -m myna run --module-root . tests/run_tests.myna
Modules imported from that root still resolve their own relative imports from their own directory. With a module root set, the entry's own directory is not searched, so a file beside the entry is imported by its path from the root (e.g.
<.tests.harness). -
myna bundle src/main.myna -o app.pyembeds the whole relative-import graph in one standalone Python file. It runs with a plainpython3and does not needmynainstalled; absolute imports still come from the environment. -
myna bundle src/main.myna --zipapp -o app.pyzwrites a runnable, executable zipapp containing real module files (python3 app.pyz). It is reproducible: rebuilding the same sources produces identical bytes. Without-o, the archive lands next to the entry file as<name>.pyz.--module-rootapplies to bundling too. -
myna compilestill compiles one file at a time. If you use relative imports, the emitted.pyfiles must be placed in a matching Python package. -
For
-t js, each relative import becomes a relative ES module specifier (./util.js, or./util.mjswhen the output ends in.mjs) and every top-level binding is exported. Compile the files into a mirrored tree, or letmyna run -t jsdo it. Node treats a.jsfile containingimport/exportas CommonJS unless the project sets"type": "module", so prefer.mjsoutputs or add that to the project'spackage.json.myna bundleremains Python-only.
JavaScript target
myna compile -t js and myna run -t js emit ES modules. Portability is opt-in: a
module without a declaration is Python-only, so add @target(py, js) to any file that
must compile for JavaScript or TypeScript (DECISIONS.md D-003). JavaScript is a
bounded target: Myna semantics are Python semantics, so the generator inlines
__myna_* helpers for truthiness, equality, %, //, #, <-, indexing, slicing, and
printing. The runtime floor is Node 20.11+ (import.meta.filename backs the @main
guard).
Unsupported constructs fail with a compile-time TargetError instead of producing wrong
output:
- single-file bundling for JavaScript (
myna bundleis Python-only; usemyna compile -t jsover the tree ormyna run -t js); - star imports (
<math:{*}); - multiple inheritance, class decorators, and method decorators;
- builtins with no faithful JavaScript equivalent (
@open,@ord,@id, …); - Python standard-library imports (they are not rewritten for Node).
Known representation limits: JavaScript has one number type (no int/float split or
arbitrary-precision integers), tuples and lists are both arrays, dictionary keys are
JavaScript property names (string-coerced), with is approximated
with try/finally + .close(), keyword arguments to user functions become a trailing
object, f-string format specs beyond .Nf are not translated, === maps to Object.is
(so primitive identity compares by value), ordered comparisons use JavaScript coercion
when operand types differ, and bitwise operators use 32-bit JavaScript semantics.
@main compiles to an import.meta.filename === process.argv[1] guard (Node's __main__
equivalent), so an imported module's entry block stays dormant.
myna targets reports
whether a file generates for a target; the test suite also executes the Node output and
runs node --check on every generated file.
TypeScript target
myna compile -t ts emits .ts (or .mts) modules. TypeScript is a typed projection
of the JavaScript target, not a third semantics: it uses the same helpers, the same
Portable Core, the same TargetErrors, and the same @target(py, js) contract. There is
no @target(ts); @target(js) blocks are emitted for TypeScript. Type annotations are
erased and never add runtime checks. myna run -t ts is not supported in this
release (use -t js to execute the same program).
Annotations become TypeScript types where Myna has them:
| Myna | TypeScript |
|---|---|
i, f |
number |
s |
string |
b |
boolean |
B |
Uint8Array |
A |
any |
N |
null |
T? |
T | null |
[T] |
T[] |
{K:V} |
Record<K, V> |
{T} |
Set<T> |
(T1, T2) |
[T1, T2] |
Name[T] |
Name<T> for TypeScript-library types; the arguments are erased for user classes and aliases |
Python typing names are translated too: Callable[[i], s] becomes
(arg0: number) => string, Mapping[K, V] becomes Record<K, V>, Sequence[T]
becomes T[], Iterator[T] becomes Iterable<T>, Awaitable[T] becomes
Promise<T>, Generator[Y, S, R] becomes Generator<Y, R, S>, Type[T] becomes
new (...args: any[]) => T, and Self resolves to the enclosing class.
ClassVar[T], Final[T], and Annotated[T, ...] erase to T; an unknown
typing.* name becomes any instead of a dangling identifier. User classes and
aliases are not generic in Myna, so Box[int] is emitted as Box (the same rule as
erased annotations).
$= Alias = Type becomes export type Alias = Type, and a relative import of a type
alias becomes import type; every other top-level binding is exported as before.
Unannotated parameters are emitted as explicit any (not implicit), and a function with
a declared return type gets a null as any fallback where Python could fall off the end.
A **kwargs parameter becomes an optional trailing object parameter, matching the
JavaScript projection's keyword-argument shape.
The guarantee is that a module whose annotations are truthful passes tsc --strict.
tsc also rejects a Myna annotation that does not match the value (for example
@f(x:i)->s => 1); myna compile still emits the file. TypeScript is advisory, like
Python annotations.
Recommended tsconfig.json for direct Node execution:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"types": ["node"]
}
}
Use .mts, or set "type": "module" in package.json, so NodeNext treats the files as
ES modules; otherwise import.meta.filename (the @main guard) is rejected. Bundler
users can use "module": "ESNext" and "moduleResolution": "Bundler" instead.
@types/node is required for @main, p, and the Uint8Array/TextEncoder helpers.
Relative-import specifiers are ./util.js (./util.mjs for .mts output), so tsc
resolves them to the generated .ts files.
The JavaScript limits apply unchanged. TypeScript-specific limits: no decorators
(NestJS/Angular-style), no JSX/TSX, no implements, and no generic declarations.
Classes only declare fields found in the class body, dataclass fields, or self.x =
assignments inside methods; dynamically added attributes need a declaration. Nested
classes are rejected. A dataclass may inherit one dataclass defined in the same module;
the subclass constructor takes the base's fields and calls super(...). Deriving a
dataclass from a class with a custom @init is rejected because there is no dataclass
constructor to forward to. Tuples and lists are both arrays, and dictionary keys are
string-coerced at runtime, so Record<K, V> is advisory; a non-key-compatible K is
emitted as Record<string, V>. Strict TypeScript also rejects dynamically retyped
locals; annotate them with A. A with ... as name binding is declared outside the
generated try so it remains visible after the statement, as in Python.
The test suite compiles the cross-target corpus with tsc --strict, transpiles it, and
executes it with Node alongside the Python output. The dev-only toolchain lives in
tests/ts/package.json; run npm ci --prefix tests/ts to enable the TypeScript tests
locally.
Token & Character Efficiency Benchmarks
Measured by compiling each example to Python and running python3 -m myna stats on the generated file (so the Myna columns reflect the decompiler's compact and -m modes). Counts use the built-in bpe-approx estimator unless tiktoken is installed:
python3 -m myna compile examples/demo.myna -o /tmp/demo.py
python3 -m myna stats /tmp/demo.py
Running stats directly on a .myna file instead compares the generated Python against that file as written, so its Myna counts will differ from the table.
| Example Program | Python Tokens / Chars | Myna (Compact) | Myna (Ultra-Compact -m) |
|---|---|---|---|
examples/demo.myna (Async Task Pipeline) |
517 toks / 953 chars |
271 toks (-47.6%) / 463 chars (-51.4%) |
247 toks (-52.2%) / 397 chars (-58.3%) |
examples/rest_api.myna (Todo HTTP REST API) |
4,105 toks / 8,011 chars |
2,183 toks (-46.8%) / 3,932 chars (-50.9%) |
2,045 toks (-50.2%) / 3,573 chars (-55.4%) |
examples/books_api.myna (Library Catalog API) |
4,402 toks / 8,515 chars |
2,371 toks (-46.1%) / 4,381 chars (-48.5%) |
2,289 toks (-48.0%) / 4,104 chars (-51.8%) |
Language Cheat Sheet
| Feature | Myna Syntax | Python Equivalent | JavaScript Equivalent |
|---|---|---|---|
| Literals | T, F, N, U, _ |
True, False, None, None, pass |
true, false, null, undefined, /*noop*/ |
| Types | i, f, s, b, B, A, T?, [T], {K:V} |
int, float, str, bool, bytes, Any, T | None, list[T], dict[K, V] |
JS strips annotations; TS emits number, string, boolean, Uint8Array, any, T | null, T[], Record<K, V> |
| Imports | <os, numpy>np / <mod:{a, b>c} |
import os, numpy as np / from mod import a, b as c |
import * as ... / import { a, b as c } from ... |
| Targets | @target(py) / @target(py) { ... } |
Python-only module / Python-only block | JS/TS-only module / block (@target(js) also applies to -t ts) |
| Functions | @f(a:i, b=1)->i { ^a+b } or @f(a)=>a+1 |
def f(a: int, b=1) -> int: return a + b |
function f(a, b = 1) { return a + b; } |
| Async / Gen | ~@f / ~expr / *@gen / ^*val |
async def / await / generator / yield |
async function / await / function* / yield |
| Classes | $C < Base { @init(x){ .x=x } } |
class C(Base): def __init__(self, x): self.x = x |
class C extends Base { constructor(x) { this.x = x; } } |
| Dataclasses | $$User(id:i, name:s="anon") |
@dataclass class User: ... |
class User { constructor(id, name = "anon") ... } |
| Dict Shorthands | {.id, .title, =name, ok: T} |
{"id": self.id, "title": self.title, "name": name, "ok": True} |
({id: this.id, title: this.title, name, ok: true}) |
| Key Lookup | d$key / d$?key ?? "def" |
d["key"] / d.get("key") or "def" |
d["key"] / d.get("key") ?? "def" |
| Branching | ? c1 {...} :? c2 {...} : {...} |
if c1: ... elif c2: ... else: ... |
if (c1) {...} else if (c2) {...} else {...} |
| Pattern Match | ?? v { 0 => {...}; x ? x>0 => {...}; _ => {...} } |
match v: case 0: ... case x if x > 0: ... case _: ... |
Block-scoped pattern & guard dispatch |
| Loops | % x <- 0..10 { ... } / %? cond { ... } |
for x in range(10): ... / while cond: ... |
for (let x = 0; x < 10; x += 1) / while (cond) |
| Comprehensions | [x*2 | x <- items ? x > 0] |
[x * 2 for x in items if x > 0] |
items.filter(x => x > 0).map(x => x * 2) |
| Exceptions | !{ ... } !!e:ValueError { ... } !: { ... } |
try: ... except ValueError as e: ... finally: ... |
try { ... } catch (e) { ... } finally { ... } |
| With / Assert | !& f=open(p) { ... } / !? cond, "msg" |
with open(p) as f: / assert cond, "msg" |
Scoped try/finally .close() / if (!cond) throw ... |
| Built-in Aliases | p, #x, @j, @J, @u8, @u8d, @srt, @sum |
print, len(x), json.loads, json.dumps, .encode, .decode, sorted, sum |
console.log, len, JSON.parse, JSON.stringify, TextEncoder, TextDecoder, .sort, .reduce |
See SPEC.md for the complete language specification and skills/myna/SKILL.md for the AI agent skill file.
Running Tests
npm ci --prefix tests/ts # dev-only TypeScript compiler + Node types
python3 -m unittest discover -s tests -v
The TypeScript tests use tests/ts/node_modules/.bin/tsc; without it (and Node on
PATH) the executable TypeScript cases are skipped, but CI installs the toolchain and
runs them.
Versioning & Design Decisions
Releases follow the policy in VERSIONING.md: during 0.x, patches are correctness fixes and release hygiene, minors carry new syntax, new capabilities, or intentional behavior changes, and 1.0 is gated on a documented, executable parity matrix. The accepted decisions behind the targets are recorded in DECISIONS.md.
License
Metadata
Release files for myna-lang 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| myna_lang-0.6.0.tar.gz | 146.9 kB | With provenance | Details for myna_lang-0.6.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| myna_lang-0.6.0-py3-none-any.whl | Python 3 | none | any | Details for myna_lang-0.6.0-py3-none-any.whl |
Total release size: 245.1 kB
Release files / myna_lang-0.6.0.tar.gz
| Download URL | myna_lang-0.6.0.tar.gz |
|---|---|
| Size | 146.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0b6bc0478a47489bdf1825513e963f083e422a04c9e5ec9f8186292aabbfdfab
|
|
BLAKE2b-256 checksum How to use checksums |
2bcc4b8e87f6f5481d0321823dbadb79580b7c4375195bf0f768f8e691cb03ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logRelease files / myna_lang-0.6.0-py3-none-any.whl
| Download URL | myna_lang-0.6.0-py3-none-any.whl |
|---|---|
| Size | 98.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
709c056aa8b3295eb5d69f4679e976c010f7890c11bc1099136d585c851b0544
|
|
BLAKE2b-256 checksum How to use checksums |
8bb17400dabeddbf6c973c2e21b5b01d1378b8253795008a7f5752977fd7137f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logSecurity for myna-lang 0.6.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for myna_lang-0.6.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.