← joyboseroy home

कारक: the grammar you learned in school, as a programming idea

A 2,500-year-old piece of Sanskrit grammar turns out to address a real problem in programming language design. This page explains the idea with examples in a script you already read, and is honest about exactly where the idea stops working. Every correction that reviewers and readers sent me is on this page. The working code and formal paper are on GitHub.

The sentence that carries the whole idea

Here is a sentence every Sanskrit textbook uses. Each word carries its role in its own ending:

रामः रावणं बाणेन हन्ति

रामः ends in ‑ः, so Rama is the doer (कर्ता). रावणं ends in ‑ं, so Ravana is the one it is done to (कर्म). बाणेन ends in ‑एन, so the arrow is the instrument (करण).

Because each word carries its own role, word order carries no meaning. All of these are the same sentence:

रावणं रामः बाणेन हन्ति
हन्ति बाणेन रावणं रामः

Every one of them parses to the identical structure:

Frame(हन्ति, karta=राम, karma=रावण, karana=बाण)

In the paper this is Theorem 1 (Order Invariance), and the proof is two lines: role binding depends only on each word's own marker, never on its position, so no permutation can change the result. The test suite exercises it directly.

आपने यह स्कूल में पढ़ा था · You learned this in school

If you studied Hindi grammar, you already know कारक. It is the chapter with ने, को, से, में. Hindi marks the same roles with small words after the noun instead of endings on it:

राम ने रावण को बाण से मारा

कारक (role)Hindi markerSanskrit endingMeaning
कर्ताने‑ःwho did it
कर्मको‑ं / ‑म्done to what
करणसे‑एनwith what tool
सम्प्रदानको / के लिए‑आयfor whom
अपादानसे‑आत्from where
अधिकरणमें / पर‑एin / at which place
संबंधका / की / के‑स्यwhose

Notice that Hindi reuses से for both instrument and source, and को for both object and recipient. Sanskrit keeps every role distinct, one ending each. That distinctness, formalized by Panini in the Ashtadhyayi around the 4th century BCE, is why this experiment borrows from Sanskrit rather than Hindi.

The programming problem

Programming languages mostly bind function inputs by position. Here is a realistic line from Prolog, a logic programming language:

causes_harm(X, Y, Z, W, V, U).

Which of the six is the person responsible? Which is the thing damaged? You cannot tell. You must find the definition and count positions. Modern languages patched this with named arguments:

connect(host="db.local", port=5432, timeout=30)

Readable, but the names are private to each function. One function calls its source src=, another from_=, another origin=. There is no shared vocabulary of roles across a program, so nothing can be written once against "the source" and apply everywhere.

Sanskrit's fix for sentences was a small, fixed set of roles that every verb shares. The experiment: use that as a calling convention for logic programming. I call it the Karaka Calling Convention (KCC), implemented in a tested project called karaka-lang.

What a program statement looks like

A legal-style fact, "the man harms the field with the flood, from the reservoir":

मनुष्यः क्षेत्राय जलेन जलाशयात् हरति

It parses into a Frame, and the same Frame compiles two ways with no translation layer, because a verb with role-labeled arguments is already the shape of a small graph: one event, labeled edges to each participant.

Frame(हरति, karta=मनुष्य, sampradana=क्षेत्र, karana=जल, apadana=जलाशय)

As Prolog facts:                As a graph-database write (Cypher):
event(e1, hara).                MERGE (e:Event {verb: "hara"})
karta(e1, man).                 MERGE (n1:Entity {name: "man"})
sampradana(e1, field).            MERGE (e)-[:KARTA]->(n1)
karana(e1, flood).              MERGE (n2:Entity {name: "flood"})
apadana(e1, reservoir).           MERGE (e)-[:KARANA]->(n2)   ...

And there is a question mode. Leave the doer open, ?कः means "who?", and the very same structure compiles to a query instead of a statement:

?कः बाणेन हन्ति

event(E, hanti), karana(E, arrow), karta(E, K).     % who kills with the arrow?

Statement or question is decided by one thing only: whether any role was left open.

What this actually buys

Here is the one demonstration the whole idea rests on. Two sentences, two different verbs:

मनुष्यः क्षेत्राय जलेन हरति (harms the field with the flood)
नरः पुत्राय धनेन ददाति (provides for the son by means of wealth)

Now one rule, which mentions no verb at all, only "has an instrument and has a recipient":

rule: requires {has:karana, has:sampradana}
      add class = "mediated-transfer"

Applied to both, straight from the project's test suite:

Frame(hara, class=mediated-transfer, karana=jal,  karta=man, sampradana=ksetr)
Frame(dada, class=mediated-transfer, karana=dhan, karta=nar, sampradana=putr)

Both classified by a single rule that knows nothing about either verb. Named arguments do not have this property. In Python, agent= means whatever each function decides. Even PropBank, the largest role-labeling system in natural language processing, defines its roles per verb: its ARG0 means "the wanter" for want, "the sleeper" for sleep, "the creator" for develop. Same label, three meanings, resolved through a lookup table. A karaka carries no lookup: कर्ता means the doer for every verb, by construction.

The honest framing is a trade, not a victory. You give up naming freedom and get cross-predicate operability, the way a database schema or a standard vocabulary like schema.org trades freedom for interoperability. Single-author code with no rule layer gains nothing from the trade. Systems where independent predicates must work together, rule engines, knowledge graphs, legal fact bases, are where it pays.

सीमाएँ · Where this honestly stops

Readers and reviewers pushed back hard, and the best objections drew the real boundary of the idea.

"Why should all functions have the same parameters?" They should not, and nothing here requires it. The killing sentence binds three roles, the flood sentence binds four. Predicates take however many roles they genuinely have. The only claim: when a predicate has a doer, it calls that argument कर्ता instead of inventing a private name. Shared vocabulary, not shared arity.

"Then map this:" a reviewer sent the best counterexample the project received:

z = OR(XOR(AND(a,b), AND(c,d)), e)

It cannot be mapped to karakas, and the reason is instructive. In AND(a,b) the inputs are interchangeable: AND(a,b) = AND(b,a). A karaka exists to carry information that distinguishes arguments; for a symmetric operator there is no information to carry, so calling a the doer would be semantically false, not just awkward. The asymmetric operators fail from the other side: subtraction's minuend and implication's antecedent are real roles, but private to their operator; there is no shared vocabulary under which "minuend" and "antecedent" are the same role. So the boundary has a precise shape: role-marking applies only where swapping arguments changes meaning and the distinctions come from a shared vocabulary of participant roles. Boolean logic fails the first test, arithmetic fails the second, and both sit outside anything this convention claims.

"What about connect(host, port, timeout)?" Also outside. Karakas are participant roles in events: who acted, on what, with what, from where. A timeout is not a participant in anything. Configuration parameters are what keyword arguments are genuinely good at, and this project does not compete there. Its territory is event-shaped data: who did what to whom, with what, from where. Legal facts, provenance, knowledge graphs.

"Your Frame(hara, karta=man, ...) looks exactly like keyword arguments anyway." It does; the syntax is indistinguishable, and that concession is in the paper. The entire difference is the one property shown above: the names are a closed set with fixed meanings across every predicate. Also: nobody needs to learn Sanskrit to use this. The roles in code are seven ASCII words; the full Sanskrit morphology layer is real but optional.

"What if a relation needs more than six roles?" A role's value can be a whole nested sub-event instead of a plain thing: "instrument: the flood" can become "instrument: a flooding event, itself caused by rainfall." Nesting removes the ceiling without inventing new roles, at the honest cost of trading a flat one-liner for a small tree.

About that NASA story

You may have heard that "NASA declared Sanskrit the best language for computers." That never happened. The real source is a genuinely interesting 1985 paper by Rick Briggs, a NASA researcher, in AI Magazine. His actual claim was narrow and useful: karaka-marked sentences are structurally equivalent to the semantic networks AI researchers were then building by hand, because case-marked roles let you drop word order without losing meaning. This project builds only on the narrow, true claim; the shuffled sentence at the top of this page is that claim, written out.

What came before, honestly

Most "Sanskrit programming languages" you will find online translate keywords: if becomes यदि, function becomes कार्य, and the grammar underneath stays C or Python. That uses nothing structural about Sanskrit. On the serious side, real computational Sanskrit exists and this project stands on it: Gerard Huet's Sanskrit Heritage Engine, the actively maintained vidyut toolkit, and sanskrit_parser, which builds karaka dependency graphs from real merged Sanskrit text.

One disclosure that matters: after I first claimed this niche was empty, a search proved me wrong. A Devanagari-first language called Karaka, by Sunil Prakash, appeared on the Rust package registry within weeks of my work, using the same six roles as labels in an imperative language. I verified this from its published source code, and the paper contains the precise comparison. Two independent implementations in one season means the idea is in the air. What this version does that his does not: roles carried by word endings rather than labels, compilation to logic facts and graph databases, the rule engine below, and the question mode above.

उत्सर्ग और अपवाद · When rules collide, the Panini way

Panini's grammar resolves conflicts with a principle every Indian grammar student meets: a general rule (उत्सर्ग) applies by default, and a more specific exception (अपवाद) overrides it. If you know CSS, this is specificity: the more specific selector wins.

The project's rule engine derives "more specific" mechanically: a rule is more specific if its required conditions strictly contain another's. When two matching rules are incomparable, neither containing the other, the engine refuses to guess; it raises an error and asks for an explicit tie-breaking rule, which is precisely how Panini's commentators handled genuinely undecided conflicts. My first version used hand-set priority numbers; reviewers pointed out that numbers do not compose when two people's rules meet, and they were right, so the numbers went.

व्याकरण ने मेरी वर्तनी सुधारी · The grammar corrected my spelling

The word endings on this page are the real thing, not decoration. Through the vidyut-prakriya engine, the project generates declensions by actually running Panini's rules. Here is देव through all seven roles:

देवःदेवम्देवेनदेवायदेवात्देवस्यदेवे

And every generated form carries a receipt: the exact Ashtadhyayi rules that produced it. "By horse," अश्वेन, derives in ten traced steps:

aSvena = aSva + Trtiya (karana)
sutras: 1.2.45 → 4.1.2 → 1.3.7 → 1.3.9 → 1.4.13
        → 7.1.12 → 1.4.18 → 1.4.14 → 6.1.87 → 8.4.68

A detail I enjoyed: while testing I misspelled "horse" with the wrong sibilant, and the engine produced a different, phonologically correct form through a real retroflexion rule instead of what I expected. The 2,500-year-old grammar caught my typo.

जो अभी बाकी है · What is still open

Three things this project has not shown, stated plainly. There is no user study yet demonstrating that role-marked facts are actually easier to read than positional Prolog; that claim is argued, not measured, and it is the biggest missing piece of evidence. The graph output is faithful going in, but a graph database will not stop a later writer from attaching two doers to one event, so the schema honesty lives in the compiler, not the store. And real Sanskrit complicates the ending-to-role mapping under passive voice, so a full system needs a per-verb surface table, whose output would still be the same fixed roles, keeping the cross-verb rules intact.

Go deeper: Code, 23 passing tests, and the formal paper (GitHub)  ·  The Medium article  ·  Back to home

Every quoted output on this page comes from the project's test suite. Corrections welcome; several sections of this page exist because someone sent one.